📌 Snapshot
- Master Python lists for CUET: indexing, slicing, methods, copying and what happens inside functions.
- Why a list can be changed in place, and how that causes aliasing, in place sorting and shared nested lists.
- Tables for append versus extend, remove versus pop, sort versus sorted, and every copying method.
- Output tracing practice that targets the favourite traps: methods returning None and one list with two names.
📖 Detailed Notes
Written and reviewed by Rohit Singh, Computer Science & Commerce faculty at UniDrill (MCA, Osmania University · 6 years teaching). Based on NCERT Computer Science Class XI, Chapter 9.
One list, two names, two surprises
Imagine you make a shopping list on paper and your friend says "give me a copy". If you hand over the same sheet and then add milk, your friend's list has milk too. If you photocopy it first, adding milk changes only yours. Python lists behave exactly like this. Writing list2 = list1 hands over the same sheet, not a photocopy. Most of the hard CUET questions on this chapter come from this one fact, together with a second one: some list methods change the list and return nothing, which makes print(list1.sort()) display None.
What is a list?
A list is an ordered sequence of one or more elements, enclosed in square brackets and separated by commas. Unlike a string, which holds only characters, a list can mix integers, floats, strings, tuples and even other lists. Most importantly, a list is mutable: its contents can be changed after it is created.
| Code |
|---|
list1 = [2, 4, 6, 8, 10, 12] |
list2 = ['a', 'e', 'i', 'o', 'u'] |
list3 = [100, 23.5, 'Hello'] |
list4 = [['Physics', 101], ['Chemistry', 202], ['Maths', 303]] |
The first is a list of even numbers, the second of vowels, the third of mixed types, and the fourth is a nested list: a list whose elements are themselves lists.
Indexing and mutability
Elements are accessed exactly like characters of a string. Indices start at 0 and run to $n - 1$ going forwards, or from $-1$ down to $-n$ going backwards. list1[15] raises IndexError: list index out of range. The index can be an expression that evaluates to an integer, such as list1[1 + 4].
Because lists are mutable, you can assign to a position.
| Code |
|---|
list1 = ['Red', 'Green', 'Blue', 'Orange'] |
list1[3] = 'Black' |
print(list1) |
| Output |
|---|
['Red', 'Green', 'Blue', 'Black'] |
Here is how a list and a string differ in the one place that matters most.
| Feature | String | List |
|---|---|---|
| Brackets | Quotes | Square brackets |
| Elements | Characters only | Any data types, even lists |
| Mutable? | No: s[1] = 'a' is a TypeError |
Yes: list1[3] = 'Black' works |
| Indexing and slicing | Same rules | Same rules |
| Slice gives | A string | A list |
List operations
| Operation | Symbol | Example | Result |
|---|---|---|---|
| Concatenation | + |
[1, 3, 5] + [2, 4] |
[1, 3, 5, 2, 4] |
| Repetition | * |
['Hello'] * 4 |
['Hello', 'Hello', 'Hello', 'Hello'] |
| Membership | in, not in |
'Green' in ['Red', 'Green'] |
True |
| Slicing | [n:m:k] |
list1[2:6] |
the elements at indices 2, 3, 4, 5 |
Concatenation and repetition build a new list and leave the originals unchanged. To keep the result you must assign it. Concatenation needs a list on both sides: [1, 2, 3] + "abc" raises a TypeError. Membership tests for a whole element, so 'Gre' in ['Red', 'Green'] is False.
Slicing follows the rules you saw for strings: start included, stop excluded, an out of range stop is trimmed, and a start beyond the stop gives an empty list.
Worked example: slicing a list. Let list1 = ['Red','Green','Blue','Cyan','Magenta','Yellow','Black'] (indices 0 to 6).
| Expression | Indices | Result |
|---|---|---|
list1[2:6] |
2, 3, 4, 5 | ['Blue', 'Cyan', 'Magenta', 'Yellow'] |
list1[2:20] |
2 to the end | ['Blue', 'Cyan', 'Magenta', 'Yellow', 'Black'] |
list1[7:2] |
none | [] |
list1[:5] |
0 to 4 | ['Red', 'Green', 'Blue', 'Cyan', 'Magenta'] |
list1[0:6:2] |
0, 2, 4 | ['Red', 'Blue', 'Magenta'] |
list1[-6:-2] |
1, 2, 3, 4 | ['Green', 'Blue', 'Cyan', 'Magenta'] |
list1[::2] |
0, 2, 4, 6 | ['Red', 'Blue', 'Magenta', 'Black'] |
list1[::-1] |
6 down to 0 | the whole list in reverse order |
Traversing a list
You can visit every element in three ways.
| Style | Code | Best when |
|---|---|---|
for over the elements |
for item in list1: |
You need the values only |
for over the indices |
for i in range(len(list1)): then list1[i] |
You need the position as well, or you want to change elements |
while loop |
i = 0, then while i < len(list1): and i += 1 |
The stopping rule is more complex |
To change elements while looping, use the index form. Assigning to the loop variable in for item in list1 only changes item, not the list.
List methods and built in functions
A method is called with a dot, such as list1.append(50). The big question for each method is: does it change the list in place, or return a new value?
Adding elements
| Method | What it does | Example | Result |
|---|---|---|---|
append(x) |
Adds one element at the end; a list stays one element | [10,20,30,40].append([50,60]) |
[10, 20, 30, 40, [50, 60]] |
extend(seq) |
Adds each element of a list at the end | [10,20,30].extend([40,50]) |
[10, 20, 30, 40, 50] |
insert(i, x) |
Puts x at index i, shifting the rest right |
[10,20,30,40,50].insert(2, 25) |
[10, 20, 25, 30, 40, 50] |
Removing elements
| Method | What it does | Example | Result |
|---|---|---|---|
remove(x) |
Deletes the first occurrence of the value; ValueError if absent | [10,20,30,40,50,30].remove(30) |
[10, 20, 40, 50, 30] |
pop(i) |
Deletes the element at index i and returns it |
[10,20,30,40,50,60].pop(3) |
returns 40; list is [10, 20, 30, 50, 60] |
pop() |
With no index, removes and returns the last element | [10,20,30,40,50,60].pop() |
returns 60 |
del list1[i] |
Deletes by index (a statement, not a method); also works with a slice | del myList[3:] on 1 to 10 |
[1, 2, 3] |
Searching and counting
| Method | What it returns | Example | Result |
|---|---|---|---|
count(x) |
How many times x appears |
[10,20,30,10,40,10].count(10) |
3 |
index(x) |
Index of the first occurrence; ValueError if absent | [10,20,30,20,40,10].index(20) |
1 |
Ordering
| Method | What it does | Returns | Changes the list? |
|---|---|---|---|
reverse() |
Reverses the order | None |
Yes, in place |
sort() |
Sorts ascending; sort(reverse=True) sorts descending |
None |
Yes, in place |
sorted(list1) |
Builds a new sorted list | The new list | No |
Measuring and converting
| Function | What it returns | Example | Result |
|---|---|---|---|
len(list1) |
Number of elements | len([10,20,30,40,50]) |
5 |
min(), max(), sum() |
Smallest, largest, total | sum([34,12,63,39,92,44]) |
284 |
list() |
An empty list | list() |
[] |
list(seq) |
A list made from any sequence | list('aeiou') |
['a', 'e', 'i', 'o', 'u'] |
Worked example: append or extend? Start with list1 = [10, 20, 30] each time.
| Statement | Result | Length |
|---|---|---|
list1.append([40, 50]) |
[10, 20, 30, [40, 50]] |
4 |
list1.extend([40, 50]) |
[10, 20, 30, 40, 50] |
5 |
Append adds its argument as a single element, so a list argument becomes a nested list. Extend unpacks its argument and adds the items one by one.
Worked example: tracing a sequence of changes. Start with L = [10, 20, 30, 40].
| Statement | Effect | L afterwards |
Value returned |
|---|---|---|---|
L.append(50) |
Adds 50 at the end | [10, 20, 30, 40, 50] |
None |
L.insert(2, 25) |
25 goes to index 2 | [10, 20, 25, 30, 40, 50] |
None |
L.remove(30) |
Deletes the value 30 | [10, 20, 25, 40, 50] |
None |
L.pop() |
Removes the last element | [10, 20, 25, 40] |
50 |
L.pop(1) |
Removes index 1 | [10, 25, 40] |
20 |
A common mistake is to confuse remove and pop: remove(30) looks for the value 30, but pop(3) removes the item at index 3.
Pause and predict: L = [3, 1, 2]. What do print(L.sort()) and then print(L) show? What if the first line were print(sorted(L))?
Check your answer: L.sort() sorts the list in place and returns None, so the first print shows None and the second shows [1, 2, 3]. With sorted(L) the first print shows the new sorted list [1, 2, 3], while L itself would still be [3, 1, 2].
Beyond the textbook. For the exam: sort() changes the list and sorted() returns a new one. In reality: the reason sort() returns None is a deliberate design choice, so that nobody mistakes an in place change for a copy. The pattern is general: in Python, methods that modify a mutable object in place return None. Whenever you see x = x.sort(), suspect a bug, because x becomes None.
Nested lists
When a list is an element of another list, you need two indices to reach inside it. list1[i][j] first picks the element i (the inner list) and then element j of that.
Worked example: reading a nested list. Let stRecord = ['Raman', 'A-36', [56, 98, 99, 72, 69], 78.8].
| What you want | Expression | Result |
|---|---|---|
| Percentage | stRecord[3] |
78.8 |
| Roll number | stRecord[1] |
'A-36' |
| Marks in the fifth subject | stRecord[2][4] |
69 |
| Highest marks | max(stRecord[2]) |
99 |
| Change the name | stRecord[0] = 'Raghav' |
the list is updated in place |
Copying lists: aliasing
The statement list2 = list1 does not create a new list. It makes list2 a second name (an alias) for the same list object. A change through either name is visible through both.
Worked example: the alias trap.
| Code |
|---|
list1 = [1, 2, 3] |
list2 = list1 |
list1.append(10) |
print(list2) |
| Step | What happens | Memory picture |
|---|---|---|
| 1 | list1 = [1, 2, 3] |
list1 refers to one list object |
| 2 | list2 = list1 |
list2 refers to the same object; no new list |
| 3 | list1.append(10) |
The shared object becomes [1, 2, 3, 10] |
| 4 | print(list2) |
[1, 2, 3, 10] |
To get a real, independent copy there are three methods.
| Method | Code | Result |
|---|---|---|
| Slicing | newList = oldList[:] |
A new, distinct list |
The list() function |
newList = list(oldList) |
A new, distinct list |
The copy module |
import copy, then newList = copy.copy(oldList) |
A new, distinct list |
After a real copy, list1.append(10) changes only list1.
Beyond the textbook. For the exam: slicing, list() and copy.copy() each make an independent copy. In reality: all three make a shallow copy. The outer list is new, but any inner lists are still shared. With a = [1, [2, 3]] and b = copy.copy(a), running b[1].append(9) changes the inner list seen through both a and b. When you need every level duplicated, use copy.deepcopy(a).
Beyond the textbook. For the exam: + creates a new list. In reality: the shorthand += behaves differently on lists: it extends the existing list in place. After a = [1, 2, 3] and b = a, the statement a = a + [4] leaves b as [1, 2, 3], because a now refers to a new list. But a += [4] changes the shared list, and b becomes [1, 2, 3, 4] too. This is why the textbook asks you to distinguish list1 * 2, list1 *= 2 and list1 = list1 * 2: the first creates a list that is thrown away, the second changes list1 itself, and the third rebinds list1 to a new list.
A list as an argument to a function
When you pass a list to a function, you pass a reference to the same list object. This leads to two cases, and the difference is a very popular question.
| Case | What the function does | Effect on the caller's list |
|---|---|---|
Changes the list in place (L[i] += 5, L.append(1)) |
Works on the shared object | Changed: the caller sees it |
Assigns a new list to the parameter (L = [15, 25], or L = L + [1]) |
The parameter now points to a new local list | Unchanged |
Worked example: in place change.
| Code |
|---|
def increment(list2): |
for i in range(0, len(list2)): |
list2[i] += 5 |
list1 = [10, 20, 30, 40, 50] |
increment(list1) |
print(list1) |
| Output |
|---|
[15, 25, 35, 45, 55] |
list2 and list1 have the same id(), so each list2[i] += 5 edits the caller's list.
Worked example: reassignment.
| Code |
|---|
def increment(list2): |
list2 = [15, 25, 35, 45, 55] |
list1 = [10, 20, 30, 40, 50] |
increment(list1) |
print(list1) |
| Output |
|---|
[10, 20, 30, 40, 50] |
The assignment makes list2 refer to a brand new list, so its id() changes and the connection to list1 is cut. The caller's list is untouched.
Pause and predict: a function contains L = L + [1] and is called with x = [5]. What is x afterwards? What if the line were L.append(1)?
Check your answer: with L = L + [1], a new list is created and stored in the local name, so x is still [5]. With L.append(1), the shared list is changed in place, so x becomes [5, 1].
Two classic list programs
Worked example: linear search. The function checks the elements one by one and returns the index where the number is found, or None if it never is.
| Code |
|---|
def linearSearch(num, list1): |
for i in range(0, len(list1)): |
if list1[i] == num: |
return i |
return None |
print(linearSearch(12, [23, 567, 12, 89, 324])) |
| Pass | i |
list1[i] |
Equal to 12? |
|---|---|---|---|
| 1 | 0 | 23 | No |
| 2 | 1 | 567 | No |
| 3 | 2 | 12 | Yes, return 2 ends the function |
The function returns index 2, which a user would report as position 3 (the program prints result + 1). return None is reached only if the loop finishes without a match.
Worked example: average marks. To average the marks of 5 students, the program starts with an empty list, uses append() inside a loop to collect each mark, then sums them in a function and divides by n. For the marks 45, 89, 79, 76, 55 the total is 344, and $344 / 5 = 68.8$.
Exam traps
| The trap | Why it's tempting | The truth |
|---|---|---|
list2 = list1 makes a copy |
The values look the same | It creates an alias; use list1[:] or list(list1) for a copy |
print(L.sort()) prints the sorted list |
Sorting sounds like it gives output | sort() returns None and sorts in place |
sorted(L) sorts L |
It sounds like sort |
It returns a new list; L is unchanged unless you assign |
append([4, 5]) adds two elements |
The list holds two items | It adds one element, a nested list; extend adds two |
remove(3) deletes the element at index 3 |
pop(3) and del L[3] do |
remove deletes by value (the first 3 it finds); pop and del delete by index |
[1, 2] + 'ab' works |
Concatenation is general | Both sides must be lists, otherwise TypeError |
L = L + [1] inside a function changes the caller's list |
Lists are mutable | It rebinds the local name; only in place changes show up |
| Slicing a list raises IndexError | A single bad index does | Slices are trimmed; [1, 2][5:] is [] |
60-second revision
| Topic | Lock this in |
|---|---|
| List | Ordered, mutable, mixed types, square brackets; indexing as in strings |
+ / * |
Need lists on both sides / repeat a list; both make a new list |
in |
Tests for an element, not a part of an element |
| Add | append (one item), extend (each item), insert(i, x) |
| Remove | remove(value), pop(index) returns the item, pop() takes the last, del L[i] or del L[a:b] |
| Find | count(x), index(x) (ValueError if absent) |
| Order | sort() and reverse() return None; sorted() returns a new list |
| Totals | len, min, max, sum |
| Nested | L[i][j] |
| Copy | = is an alias; [:], list(), copy.copy() are shallow copies |
| In a function | In place changes are seen by the caller; reassigning the parameter is not |
| Linear search | Compare one by one, return the index or None |
Where this chapter connects
- Previous: Strings introduced indexing, slicing, concatenation and membership, all of which work the same way on lists.
- Next: Tuples and Dictionaries compares the mutable list with the immutable tuple, and adds the key based dictionary.
- Also related: Functions explains parameters and return values, and Working with Lists and Dictionaries in the Informatics Practices course extends the same ideas.
🎯 Practice MCQs
First 3 questions free · create a free account to unlock the rest — answers & explanations included, no payment needed
Q1. If L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], what is L[::-2]?
▸ Show answer & explanation
Answer: A
A negative step starts from the last element and moves left two places at a time: 10, 8, 6, 4, 2. Option B would start from the second last element, and option D goes left to right.
Q2. Consider: (1) list1 = [10, 20, 30] (2) list1.append([40, 50]) (3) print(len(list1)). What is printed?
▸ Show answer & explanation
Answer: B
The append method adds its argument as one single element, so the list becomes [10, 20, 30, [40, 50]], which has 4 elements. Option C would be the result of extend.
Q3. Assertion (A): print(L.sort()) displays None. Reason (R): The sort method arranges the elements of the list in place and returns nothing.
▸ Show answer & explanation
Answer: A
The sort method changes the original list and returns None. Printing its return value therefore shows None, so R explains A.
🔒 12 more practice MCQs
Create a free account to unlock every MCQ in this chapter — answers and explanations included. No payment needed.
Already registered? Just log in and they'll all appear here.
Q4. Match List I with List II. List I: (i) pop() (ii) remove(30) (iii) insert(1, 15) (iv) extend([1, 2]). List II: (p) removes the first occurrence of a value (q) removes and returns the last element (r) adds one element at a given index (s) adds each element of another list at the end.
▸ Show answer & explanation
Answer: C
With no index, pop removes and returns the last element. remove deletes the first occurrence of a given value. insert places one element at the stated index, and extend adds every element of its argument at the end. Option A swaps pop and remove.
Q5. Consider: (1) a = [1, 2, 3] (2) b = a (3) a.append(10) (4) print(b). What is printed?
▸ Show answer & explanation
Answer: B
The statement b = a makes b another name for the same list, not a copy. The append changes that shared list, so b shows the new element too.
Q6. Consider: (1) a = [1, 2, 3] (2) b = a (3) a = a + [4] (4) print(b). What is printed?
▸ Show answer & explanation
Answer: B
The expression a + [4] builds a new list, and the name a is rebound to it. The name b still refers to the original list, which was never changed. If statement (3) were a += [4], the shared list would change and the answer would be option A.
Q7. Consider: (1) def f(L): (2) L = L + [1] (3) x = [5] (4) f(x) (5) print(x). Statement (2) is the body of f. What is printed?
▸ Show answer & explanation
Answer: B
The expression L + [1] creates a new list that is stored only in the local name L. The caller's list x is not modified. Option A would be the result if the body used L.append(1).
Q8. If list1 = [1, 2, 'a', 'c', [6, 7, 8], 4, 9], what is list1[4][1]?
▸ Show answer & explanation
Answer: B
The index 4 selects the nested list [6, 7, 8], and the index 1 then picks its second element, 7. Option C is list1[3], and option A is list1[4][0].
Q9. Consider: (1) myList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] (2) del myList[::2] (3) print(myList). What is printed?
▸ Show answer & explanation
Answer: B
The slice [::2] selects the elements at indices 0, 2, 4, 6 and 8, which are 1, 3, 5, 7 and 9. Deleting them leaves the even numbers. Option A is what the slice selected, and option C is the result of del myList[3:].
Q10. For L = [10, 20, 30, 20, 40, 10], what is the value of L.index(20) + L.count(10)?
▸ Show answer & explanation
Answer: A
The first 20 is at index 1, and the value 10 occurs twice. So the sum is 1 + 2 = 3. Option B would be right if index returned the position of the last 20.
Q11. Which of the following statements are correct? (i) A list can contain elements of different data types. (ii) The expression [1, 2] + 'ab' joins the list and the string. (iii) Slicing a list returns a new list. (iv) remove(x) deletes every occurrence of x.
▸ Show answer & explanation
Answer: A
Statements (i) and (iii) are true. Statement (ii) raises a TypeError because concatenation needs lists on both sides. Statement (iv) is false because remove deletes only the first occurrence.
Q12. Consider: (1) list1 = [23, 45, 11] (2) sorted(list1) (3) print(list1). What is printed?
▸ Show answer & explanation
Answer: B
The sorted function returns a new sorted list and does not change the original. Since the result in statement (2) is not stored, list1 is still [23, 45, 11].
Q13. Consider: (1) list1 = [6, 7, 8, 9] (2) list2 = list1 repeated 2 times, using the repetition operator (3) print(len(list1), len(list2)). What is printed?
▸ Show answer & explanation
Answer: C
Repetition creates a new list with the elements repeated, so list2 has 8 elements. The original list1 remains unchanged with 4 elements. Option B would be the result if list1 itself were repeated in place.
Q14. Consider: (1) myList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] (2) for i in range(0, len(myList)): (3) if i % 2 == 0: print(myList[i]), where statement (3) is the loop body. Which values are printed?
▸ Show answer & explanation
Answer: B
The test is on the index i, not on the value. Indices 0, 2, 4, 6 and 8 hold the values 1, 3, 5, 7 and 9. Option C confuses the indices with the values.
Q15. If stRecord = ['Raman', 'A-36', [56, 98, 99, 72, 69], 78.8], what is stRecord[2][4]?
▸ Show answer & explanation
Answer: B
The index 2 selects the list of marks, and the index 4 picks its fifth element, 69. Option A is the fourth element, and option C is the maximum, not the fifth.
📊 Previous-Year Questions
Practise with real CUET Computer Science previous-year papers — every question solved, with the correct answer and a step-by-step explanation.
View solved CUET PYQ papers →Ready to drill Computer Science?
Unlock all MCQs, chapter tests, mocks & PYQs for ₹199/year.
Get UniDrill Pro