Python Lists - Part 1
Before starting this lesson, let’s give you a programming task: roll a die 6000 times and count the number of times each point appears. This task should be very simple for you. We can use uniformly distributed random numbers from 1 to 6 to simulate rolling a die, then use 6 variables to record the number of times each point appears. I believe that through previous learning, you can all write the following code relatively smoothly.
"""
Roll a die 6000 times and count the number of times each point appears
Author: Luo Hao
Version: 1.0
"""
import random
f1 = 0
f2 = 0
f3 = 0
f4 = 0
f5 = 0
f6 = 0
for _ in range(6000):
face = random.randrange(1, 7)
if face == 1:
f1 += 1
elif face == 2:
f2 += 1
elif face == 3:
f3 += 1
elif face == 4:
f4 += 1
elif face == 5:
f5 += 1
else:
f6 += 1
print(f'1 point appeared {f1} times')
print(f'2 points appeared {f2} times')
print(f'3 points appeared {f3} times')
print(f'4 points appeared {f4} times')
print(f'5 points appeared {f5} times')
print(f'6 points appeared {f6} times')
I don’t need to say much about how “ugly” the code above is. Of course, what’s even more terrible is that if we want to roll two or more dice and then count the number of times each point appears, we would need to define more variables and write more branch structures. Just thinking about it makes you feel sick. At this point, I believe you already have a question in mind: is there a way to use one variable to store multiple data, and is there a way to use unified code to operate on multiple data? The answer is yes. In Python, we can use container-type variables to store and operate on multiple data. We’ll first introduce the list (list) data type.
Creating Lists
In Python, a list is a data sequence composed of a series of elements in a specific order. This means that if we define a list-type variable, we can use it to store multiple data items. In Python, we can use [] literal syntax to define lists, with multiple elements in the list separated by commas, as shown in the code below.
items1 = [35, 12, 99, 68, 55, 35, 87]
items2 = ['Python', 'Java', 'Go', 'Kotlin']
items3 = [100, 12.3, 'Python', True]
print(items1) # [35, 12, 99, 68, 55, 35, 87]
print(items2) # ['Python', 'Java', 'Go', 'Kotlin']
print(items3) # [100, 12.3, 'Python', True]
Note: Lists can have duplicate elements, such as
35initems1; lists can have elements of different types, such asitems3which hasint,float,str, andbooltype elements. However, we generally don’t recommend putting elements of different types in the same list, mainly because it’s extremely inconvenient to operate on them.
We can use the type function to check the type of a variable. Interested readers can check what type the variable items1 above is. Because lists can store multiple elements, they are a container-type data type, so when naming list-type variables, we usually use plural forms of words.
In addition, we can also use Python’s built-in list function to convert other sequences into lists. To be precise, list is not an ordinary function; it’s a constructor for creating list objects. We’ll introduce concepts like objects and constructors in later lessons.
items4 = list(range(1, 10))
items5 = list('hello')
print(items4) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(items5) # ['h', 'e', 'l', 'l', 'o']
Note:
range(1, 10)produces an integer sequence from1to9. When given to thelistconstructor, it creates a list consisting of integers from1to9. A string is a sequence of characters, solist('hello')above uses the characters from the stringhelloas list elements to create a list object.
List Operations
We can use the + operator to concatenate two lists. The concatenation operation connects the elements from two lists into one list, as shown in the code below.
items5 = [35, 12, 99, 45, 66]
items6 = [45, 58, 29]
items7 = ['Python', 'Java', 'JavaScript']
print(items5 + items6) # [35, 12, 99, 45, 66, 45, 58, 29]
print(items6 + items7) # [45, 58, 29, 'Python', 'Java', 'JavaScript']
items5 += items6
print(items5) # [35, 12, 99, 45, 66, 45, 58, 29]
We can use the * operator to repeat list elements. The * operator repeats list elements a specified number of times. Let’s add two more lines to the code above, as shown below.
print(items6 * 3) # [45, 58, 29, 45, 58, 29, 45, 58, 29]
print(items7 * 2) # ['Python', 'Java', 'JavaScript', 'Python', 'Java', 'JavaScript']
We can use the in or not in operators to determine whether an element is in a list. Let’s add two more lines to the code above, as shown below.
print(29 in items6) # True
print(99 in items6) # False
print('C++' not in items7) # True
print('Python' not in items7) # False
Since a list has multiple elements and the elements are placed in the list in a specific order, when we want to operate on a specific element in the list, we can use the [] operator. By specifying the element’s position in [], we can access that element. This operation is called indexing. It should be noted that the element position in [] can be an integer from 0 to N - 1, or an integer from -1 to -N, called forward indexing and reverse indexing respectively, where N represents the number of list elements. For forward indexing, [0] accesses the first element in the list, and [N - 1] accesses the last element; for reverse indexing, [-1] accesses the last element in the list, and [-N] accesses the first element, as shown in the code below.
items8 = ['apple', 'waxberry', 'pitaya', 'peach', 'watermelon']
print(items8[0]) # apple
print(items8[2]) # pitaya
print(items8[4]) # watermelon
items8[2] = 'durian'
print(items8) # ['apple', 'waxberry', 'durian', 'peach', 'watermelon']
print(items8[-5]) # 'apple'
print(items8[-4]) # 'waxberry'
print(items8[-1]) # watermelon
items8[-4] = 'strawberry'
print(items8) # ['apple', 'strawberry', 'durian', 'peach', 'watermelon']
When using indexing operations, avoid index out of bounds situations. For items8 above, if we access items8[5] or items8[-6], it will raise an IndexError error, causing the program to crash. The corresponding error message is: list index out of range. For a list with only five elements like items8, valid forward indices are 0 to 4, and valid reverse indices are -1 to -5.
If we want to access multiple elements in a list at once, we can use slicing operations. A slicing operation is an operator of the form [start:end:stride], where start represents the starting position for accessing list elements, end represents the ending position for accessing list elements (the element at the ending position cannot be accessed), and stride represents the step size, simply put, the position increment. For example, if the first element we access is at the start position, then the second element is at the start + stride position, and of course start + stride must be less than end. Let’s add the following statements to the code above to use the slicing operator to access list elements.
print(items8[1:3:1]) # ['strawberry', 'durian']
print(items8[0:3:1]) # ['apple', 'strawberry', 'durian']
print(items8[0:5:2]) # ['apple', 'durian', 'watermelon']
print(items8[-4:-2:1]) # ['strawberry', 'durian']
print(items8[-2:-6:-1]) # ['peach', 'durian', 'strawberry', 'apple']
Reminder: Take a look at the last line in the code above and think about how slicing operations access elements when the stride is negative.
If the start value equals 0, it can be omitted when using the slicing operator; if the end value equals N, where N represents the number of list elements, it can be omitted when using the slicing operator; if the stride value equals 1, it can also be omitted when using the slicing operator. Therefore, the following code has exactly the same effect as the code above.
print(items8[1:3]) # ['strawberry', 'durian']
print(items8[:3:1]) # ['apple', 'strawberry', 'durian']
print(items8[::2]) # ['apple', 'durian', 'watermelon']
print(items8[-4:-2]) # ['strawberry', 'durian']
print(items8[-2::-1]) # ['peach', 'durian', 'strawberry', 'apple']
In fact, we can also modify elements in a list through slicing operations. For example, let’s add one more line to the code above and see the output.
items8[1:3] = ['x', 'o']
print(items8) # ['apple', 'x', 'o', 'peach', 'watermelon']
Two lists can also perform relational operations. We can compare whether two lists are equal, and we can also compare the size of two lists, as shown in the code below.
nums1 = [1, 2, 3, 4]
nums2 = list(range(1, 5))
nums3 = [3, 2, 1]
print(nums1 == nums2) # True
print(nums1 != nums2) # False
print(nums1 <= nums3) # True
print(nums2 >= nums3) # False
Note: In the code above,
nums1andnums2have completely identical corresponding elements, so the result of the==operation isTrue. For the comparison ofnums2andnums3, since the first element1ofnums2is less than the first element3ofnums3, the result ofnums2 >= nums3comparison isFalse. Relational operations on two lists are not that commonly used in actual work. If you really don’t understand, just skip it; don’t get hung up on it.
Element Iteration
If you want to retrieve elements from a list one by one, you can use a for-in loop. There are two ways to do this.
Method 1: Iterate through list elements using indexing operations in the loop structure.
languages = ['Python', 'Java', 'C++', 'Kotlin']
for index in range(len(languages)):
print(languages[index])
Output:
Python
Java
C++
Kotlin
Note: The
lenfunction above can get the number of list elementsN, andrange(N)constructs a range from0toN-1, which can be used as indices for list elements.
Method 2: Loop directly over the list, with the loop variable representing list elements.
languages = ['Python', 'Java', 'C++', 'Kotlin']
for language in languages:
print(language)
Output:
Python
Java
C++
Kotlin
Summary
At this point, we can use list knowledge to refactor the code for “rolling a die and counting the number of times each point appears”.
"""
Roll a die 6000 times and count the number of times each point appears
Author: Luo Hao
Version: 1.1
"""
import random
counters = [0] * 6
# Simulate rolling a die and record the number of times each point appears
for _ in range(6000):
face = random.randrange(1, 7)
counters[face - 1] += 1
# Output the number of times each point appeared
for face in range(1, 7):
print(f'{face} point(s) appeared {counters[face - 1]} times')
In the code above, we use the six elements in the counters list to represent the number of times points 1 to 6 appear, with all six elements initially set to 0. Next, we use uniformly distributed random numbers from 1 to 6 to simulate rolling a die. If we roll 1 point, the value of counters[0] increases by 1; if we roll 2 points, the value of counters[1] increases by 1, and so on. Feel the difference - by using a list type combined with a loop structure, our data processing is batch-oriented, which makes the modified code much simpler and more elegant than the previous code.