Details of Lists (Part 1)
Composite data types are fundamental to Python programming, and mastering their characteristics and use cases is key to writing efficient Python code. Unlike simple data types such as numbers (int/float), composite data types are primarily used to organize and manage a group of related data.Today, we will learn about the first part of lists, including list construction, adding and removing elements, traversing lists, safely deleting elements while traversing, and further reading on lists will be covered in the next note.
Python provides five core composite data types, each with its own characteristics:
- • List: An ordered, mutable, and repeatable sequence, represented by []
- • Tuple: An ordered, immutable, and repeatable sequence, represented by ()
- • Dictionary: An unordered (insertion ordered in Python 3.7+), mutable collection of key-value pairs, represented by {}
- • Set: An unordered, mutable, and non-repeatable collection of elements, represented by {} or set()
- • String: Although strings are an extension of basic data types, due to their immutability and sequence characteristics, they are sometimes also considered complex types.
Lists
Lists are mutable ordered sequences that can contain objects of any type simultaneously. They are suitable for storing ordered data that requires frequent modification of elements. Next, we will learn about adding, deleting, modifying, querying, sorting, and slicing lists.
1. Creating and Accessing Lists
Lists are ordered, elements can be repeated, and accessing elements in a list is done using [index]. Lists can be created using list comprehensions or by creating an empty list and appending elements based on conditions.Let’s look at various methods of creating lists: [], list(), and comprehensions can all be used.
my_list1 = []
my_list1.append(1)
my_list2 = [1]
my_list2.append(1)
print(my_list1) #[1]
print(my_list2) #[1, 1]
# Accessing elements by index
print(f"{my_list2[0]=}, {my_list2[1]=}")
# Output: my_list2[0]=1, my_list2[1]=1
# Mixed types in a list
mixed_list = [1, "apple", 3.14, True]
print(mixed_list)
# Creating using constructor
str_to_list = list("hello") # ['h', 'e', 'l', 'l', 'o']
# List comprehension to generate a list
my_list3 = [x**2 for x in range(1, 11)]
print(my_list3)#[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# Adding even numbers condition
my_list4 = [x for x in range(1, 11) if x % 2 == 0]
print(my_list4)#[2, 4, 6, 8, 10]
# Getting multiples of 3, 6, 9 from 1-20
my_list5 = [x for x in range(1, 21)
if any(x % y == 0 for y in [3, 6, 9])]
print(my_list5)#[3, 6, 9, 12, 15, 18]
2. Adding Elements to Lists
Elements can be added using append(), extend(), insert(); append() adds a single element to the end of the list, regardless of the type of object being appended, it will be added as a single element at the end of the list.
#append
my_list1 = list(range(1, 11))
my_list1.append(11)
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
print(my_list1)
my_list1.append(range(12, 21))
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, range(12, 21)]
print(my_list1)
extend() can only take an iterable as an argument and adds all elements of the iterable to the end of the list, used for list concatenation and extension;
#extend
my_list1 = list(range(1, 11))
my_list1.extend("="*3)
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, '=', '=', '=']
print(my_list1)
my_list1.extend(range(12, 21))
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
# '=', '=', '=', 12, 13, 14, 15, 16, 17, 18, 19, 20]
print(my_list1)
my_list2 = list(range(1, 11))
my_list2.extend(list(range(11, 21)))
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
# 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
print(my_list2)
insert() inserts an element at a specified index;
#insert
nums = [1, 3, 4]
# Insert 2 at index 1 → [1, 2, 3, 4]
nums.insert(1, 2)
# Insert 0 at the beginning → [0, 1, 2, 3, 4]
nums.insert(0, 0)
# Insert at the end (equivalent to append) → [0, 1, 2, 3, 4, 5]
nums.insert(len(nums), 5)
+ operator: Concatenates two lists (creates a new list, original lists remain unchanged);
a = [1, 2]
b = [3, 4]
c = a + b # [1, 2, 3, 4]
print(a) # Original list a remains unchanged [1, 2]
print(c) # [1, 2, 3, 4]
Next, we will summarize the various ways to add elements to Python lists and their applicable scenarios in a table.
| Method/Operation | Core Operation | Applicable Scenario | Efficiency (Time Complexity) |
| append(x) | Adds a single element x to the end of the list | Single element addition, commonly used for adding one by one in a loop | O(1) |
| extend(iter) | Adds elements from the iterable iter one by one to the end of the list | Merging multiple elements (like list concatenation), avoiding loop calls to append | O(n) |
| insert(i, x) | Inserts element x at the specified index i, shifting subsequent elements to the right | Need to insert an element at a specific position (like at the beginning or in the middle) | O(n) |
| + operator | Connects two lists, creates a new list, original lists remain unchanged | Need to keep the original list unchanged, or directly create a new list | O(n+m) (n, m are the lengths of the two lists) |
| += operator | Similar to extend, but will modify the original list directly (equivalent to extend) | Need to merge lists in place and keep the code concise | O(n) |
Important note: In Python, a = a + b and a += b have different execution principles for mutable and immutable objects, which is different from C series languages. In C/C++: variables are aliases for memory addresses; in Python: variables are references to objects,a = a + b will create a new object and then let a point to the new object (even for int types). For a += b, the behavior of += in Python is determined by the mutability of the object. For immutable objects: cannot be modified in place, += is equivalent to a = a + b, creating a new object and letting a point to it.For mutable objects: += modifies the original object in place (calling the __iadd__ method), does not create a new object, and the address remains unchanged.The root difference: C/C++ is based on a memory model, Python is based on an object reference model.
# Let's look at a = a + b
a = [1, 2]
b = [3, 4]
c = a
print(f"The Id of a {id(a)}, {a=}")
print(f"The Id of c {id(c)}, {c=}")
# Output: The Id of a 1964179739136, a=[1, 2]
# Output: The Id of c 1964179739136, c=[1, 2]
a = a + b
print(f"The Id of a {id(a)}, {a=}")
print(f"The Id of c {id(c)}, {c=}")
# Output: The Id of a 1964179543424, a=[1, 2, 3, 4]
# Output: The Id of c 1964179739136, c=[1, 2]
"""
Now let's look at a += b, which extends a at the original memory address of the referenced object.
The specific behavior depends on the nature of the referenced object:
For immutable objects, a new object will be created.
For mutable objects, no new object will be created.
"""
a = [1, 2]
b = [3, 4]
c = a
print(f"The Id of a {id(a)}, {a=}")
print(f"The Id of c {id(c)}, {c=}")
# Output: The Id of a 1799589817472, a=[1, 2]
# Output: The Id of c 1799589817472, c=[1, 2]
a += b
print(f"The Id of a {id(a)}, {a=}")
print(f"The Id of c {id(c)}, {c=}")
# Output: The Id of a 1799589817472, a=[1, 2, 3, 4]
# Output: The Id of c 1799589817472, c=[1, 2, 3, 4]
3. Deleting Elements from Lists
Elements can be deleted using remove(), del(), pop(), clear();
remove() deletes by value, removing the first matching specified value and stopping there; if the value does not exist, it will raise an error. When using, ensure that the value to be deleted exists.
nums = [1, 2, 3, 2]
ums.remove(2) # Remove the first 2 → [1, 3, 2]
print(nums) # [1, 3, 2]
# nums.remove(4) # Raises: ValueError: list.remove(x): x not in list
pop() deletes by index, removing the element at the specified index (default is the last one) and returning that element; see below for the official documentation of the pop function, which defaults to removing the element at index -1, which is the last element of the list. If the list is empty or the index is out of range, it will raise an exception.
(method) def pop(
index: SupportsIndex = -1,
/=-1
)
Remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
# Testing the pop function
numbers = list(range(1, 11))
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers)
pop_item = numbers.pop(0)
# Output: pop_item=1, nums=[2, 3, 4, 5, 6, 7, 8, 9, 10]
print(f"{pop_item=}, {numbers=}")
pop_item = numbers.pop()
# Output: pop_item=10, nums=[2, 3, 4, 5, 6, 7, 8, 9]
print(f"{pop_item=}, {numbers=}")
del() deletes elements at specified index/slice or deletes the entire list.
numbers = list(range(11))
# Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers)
# Deleting a single element
del numbers[0]
# Output: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers)
# Deleting a slice range
numbers = list(range(11))
del numbers[0:3]
# Slices are left-closed and right-open intervals, deleting elements at indices 0, 1, 2
# Output: [3, 4, 5, 6, 7, 8, 9, 10]
print(numbers)
clear() clears the list.
numbers = list(range(11))
# Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers)
# Clearing the list
numbers.clear()
# Output: []
print(numbers)
List Comprehensions If you want to delete some elements from a list without knowing the index, value, or slice arrangement, but know the condition for deletion, you can use list comprehensions to create a new list, filtering out the elements that do not need to be deleted, allowing the list variable to reference the newly generated list object, which meets the requirement of deleting elements that satisfy certain conditions.
# For example, deleting multiples of 3 from the list
numbers = list(range(11))
# Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers)
# Deleting multiples of 3
numbers = [number for number in numbers if number % 3 != 0]
# Output: [1, 2, 4, 5, 7, 8, 10]
print(numbers)
filter() to filter iterable objects based on conditions Similar to deleting elements from a list that meet certain conditions, you can also use filter() to achieve the same effect. Compared to list comprehensions, filter can take a filtering function, encapsulating the filtering logic in a function, allowing those that satisfy the function to remain. This can be easier to understand when writing more complex filtering logic, but list comprehensions can also directly complete element transformations, each having its pros and cons. The lazy evaluation characteristics of filter() parameters and return values will be discussed in detail in our later notes.
# For example, deleting multiples of 3 from the list
def number_selector(number) -> bool:
return number % 3 != 0
# For example, deleting multiples of 3 from the list
numbers = list(range(11))
# Output: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers)
# Deleting multiples of 3
numbers = list(filter(number_selector, numbers))
# Output: [1, 2, 4, 5, 7, 8, 10]
print(numbers)
# You can also choose a lambda function instead of a custom function
numbers = list(range(11))
numbers = list(filter(lambda x: x % 3 != 0, numbers))
# Output: [1, 2, 4, 5, 7, 8, 10]
print(numbers)
Summary of Usage Scenarios
| Method | Applicable Scenario | Modifies Original List | Return Value |
| remove() | Know the value to delete | Yes | None |
| pop() | Know the index position | Yes | Deleted element |
| del | Know the index or slice | Yes | None |
| clear() | Clear the entire list | Yes | None |
| List Comprehensions | Conditionally delete multiple elements | Creates a new list | New list |
| filter() | Functionally conditionally delete | Creates a new list | New list |
If you find this article helpful, please give it a thumbs up and a heart, as it is the greatest encouragement for me. If you find it helpful or if there are any inaccuracies in the content, please leave a comment to let me know. Thank you!