Python Lists - Part 2

by Pyrastra Team
Python Lists - Part 2

List Methods

List-type variables have many methods that help us operate on lists. Assuming we have a list named foos and the list has a method named bar, the syntax for using list methods is: foos.bar(). This is a syntax for calling object methods through object references. We’ll explain this syntax in detail later when we cover object-oriented programming. This syntax is also called sending messages to objects.

Adding and Removing Elements

Lists are mutable containers, meaning we can add elements to the container, remove elements from the container, and modify existing elements in the container. We can use the list’s append method to append elements to the list, and use the insert method to insert elements into the list. Appending means adding an element to the end of the list, while inserting means adding a new element at a specified position. You can see the code below.

languages = ['Python', 'Java', 'C++']
languages.append('JavaScript')
print(languages)  # ['Python', 'Java', 'C++', 'JavaScript']
languages.insert(1, 'SQL')
print(languages)  # ['Python', 'SQL', 'Java', 'C++', 'JavaScript']

We can use the list’s remove method to delete a specified element from the list. Note that if the element to be deleted is not in the list, it will raise a ValueError error causing the program to crash, so we recommend checking with membership operations before deleting elements. We can also use the pop method to delete elements from the list. The pop method deletes the last element in the list by default, but you can also provide a position to delete the element at a specified position. When using the pop method to delete elements, if the index value is out of range, it will raise an IndexError exception, causing the program to crash. In addition, lists also have a clear method that can clear all elements in the list, as shown in the code below.

languages = ['Python', 'SQL', 'Java', 'C++', 'JavaScript']
if 'Java' in languages:
    languages.remove('Java')
if 'Swift' in languages:
    languages.remove('Swift')
print(languages)  # ['Python', 'SQL', 'C++', 'JavaScript']
languages.pop()
temp = languages.pop(1)
print(temp)       # SQL
languages.append(temp)
print(languages)  # ['Python', 'C++', 'SQL']
languages.clear()
print(languages)  # []

Note: The pop method returns the deleted element when deleting. In the code above, we assigned the element deleted by the pop method to a variable named temp. Of course, if you want, you can add this element back to the list, just as the code languages.append(temp) does above.

There’s a small question here: if the languages list has multiple 'Python' entries, does languages.remove('Python') delete all 'Python' entries or just the first one? You can guess first, then try it yourself.

There’s actually another way to delete elements from a list, which is to use Python’s del keyword followed by the element to delete. This approach is not substantially different from using the pop method with a specified index to delete elements, but the latter returns the deleted element, while the former is slightly better in performance because del corresponds to the underlying bytecode instruction DELETE_SUBSCR, while pop corresponds to the underlying bytecode instructions CALL_METHOD and POP_TOP. If you don’t understand, don’t worry about it.

items = ['Python', 'Java', 'C++']
del items[1]
print(items)  # ['Python', 'C++']

Element Position and Frequency

The list’s index method can find the index position of an element in the list. If the specified element cannot be found, the index method will raise a ValueError error. The list’s count method can count the number of times an element appears in the list, as shown in the code below.

items = ['Python', 'Java', 'Java', 'C++', 'Kotlin', 'Python']
print(items.index('Python'))     # 0
# Start searching for 'Python' from index position 1
print(items.index('Python', 1))  # 5
print(items.count('Python'))     # 2
print(items.count('Kotlin'))     # 1
print(items.count('Swift'))      # 0
# Start searching for 'Java' from index position 3
print(items.index('Java', 3))    # ValueError: 'Java' is not in list

Element Sorting and Reversing

The list’s sort operation can sort list elements, while the reverse operation can reverse elements, as shown in the code below.

items = ['Python', 'Java', 'C++', 'Kotlin', 'Swift']
items.sort()
print(items)  # ['C++', 'Java', 'Kotlin', 'Python', 'Swift']
items.reverse()
print(items)  # ['Swift', 'Python', 'Kotlin', 'Java', 'C++']

List Comprehensions

In Python, lists can also be created through a special literal syntax called comprehensions. Let’s use examples to illustrate the benefits of using list comprehensions to create lists.

Scenario 1: Create a list of numbers in the range 1 to 99 that are divisible by 3 or 5.

items = []
for i in range(1, 100):
    if i % 3 == 0 or i % 5 == 0:
        items.append(i)
print(items)

Using list comprehension to do the same thing, the code is as follows.

items = [i for i in range(1, 100) if i % 3 == 0 or i % 5 == 0]
print(items)

Scenario 2: There’s an integer list nums1. Create a new list nums2 where the elements are the squares of the corresponding elements in nums1.

nums1 = [35, 12, 97, 64, 55]
nums2 = []
for num in nums1:
    nums2.append(num ** 2)
print(nums2)

Using list comprehension to do the same thing, the code is as follows.

nums1 = [35, 12, 97, 64, 55]
nums2 = [num ** 2 for num in nums1]
print(nums2)

Scenario 3: There’s an integer list nums1. Create a new list nums2 that contains elements from nums1 that are greater than 50.

nums1 = [35, 12, 97, 64, 55]
nums2 = []
for num in nums1:
    if num > 50:
        nums2.append(num)
print(nums2)

Using list comprehension to do the same thing, the code is as follows.

nums1 = [35, 12, 97, 64, 55]
nums2 = [num for num in nums1 if num > 50]
print(nums2)

Creating lists using list comprehensions is not only simple and elegant in code, but also superior in performance to using for-in loops and the append method to add elements to an empty list. Why do comprehensions have better performance? That’s because Python interpreter’s bytecode instructions have instructions specifically for comprehensions (the LIST_APPEND instruction); while for loops add elements to lists through method calls (LOAD_METHOD and CALL_METHOD instructions), and method calls themselves are relatively time-consuming operations. If you don’t understand this, it doesn’t matter. Just remember the conclusion: “strongly recommend using comprehension syntax to create lists”.

Nested Lists

Python doesn’t require that elements in a list must be of the same data type, which means elements in a list can be of any data type, including lists themselves. If elements in a list are also lists, we can call it a nested list. Nested lists can be used to represent tables or matrices in mathematics. For example, if we want to store the grades of 5 students in 3 courses, we can use a list as shown below.

scores = [[95, 83, 92], [80, 75, 82], [92, 97, 90], [80, 78, 69], [65, 66, 89]]
print(scores[0])
print(scores[0][1])

For the nested list above, each element is essentially one student’s grades in 3 courses, such as [95, 83, 92], and 83 in this list represents this student’s grade in a certain course. If you want to access this value, you can use indexing twice: scores[0][1], where scores[0] gets the list [95, 83, 92], and using the indexing operation [1] again gets the second element in that list.

If you want to input grades for 5 students in 3 courses through keyboard input and save them in a list, you can use the code shown below.

scores = []
for _ in range(5):
    temp = []
    for _ in range(3):
        score = int(input('Please enter: '))
        temp.append(score)
    scores.append(temp)
print(scores)

If you want to generate grades for 5 students in 3 courses by generating random numbers and save them in a list, we can use list comprehension, as shown in the code below.

import random

scores = [[random.randrange(60, 101) for _ in range(3)] for _ in range(5)]
print(scores)

Note: In the code above, [random.randrange(60, 101) for _ in range(3)] can produce a list consisting of 3 random integers. We placed this code in another list comprehension as list elements, and such elements are generated 5 times, ultimately obtaining a nested list.

List Applications

Let’s explain list applications through an example of random lottery number selection. The Double Color Ball is a lottery-type lottery issued by the China Welfare Lottery Issuance Management Center. Each bet consists of 6 red balls and 1 blue ball. Red ball numbers are selected from 1 to 33, and blue ball numbers are selected from 1 to 16. Each bet requires selecting 6 red ball numbers and 1 blue ball number, as shown below.

lottery

Tip: There’s a brilliant discussion on Zhihu about the essence of various forms of domestic lotteries, which I’ll share with you here: “Fabricate a person who gets something for nothing, to fool a group of people who want something for nothing, ultimately supporting a group of people who truly get something for nothing”. Many people who have no concept of probability even think that the probability of winning or not winning the lottery is both 50%; there are also many people who think that if the probability of winning is 1%, then buying 100 times will definitely win. These are all very absurd ideas. So, cherish life, stay away from gambling, especially when you know nothing about probability!

Below, we’ll generate a set of random numbers through a Python program.

"""
Double Color Ball random number selection program

Author: Luo Hao
Version: 1.0
"""
import random

red_balls = list(range(1, 34))
selected_balls = []
# Add 6 red balls to the selected list
for _ in range(6):
    # Generate a random integer representing the index position of the selected red ball
    index = random.randrange(len(red_balls))
    # Remove the selected ball from the red ball list and add it to the selected list
    selected_balls.append(red_balls.pop(index))
# Sort the selected red balls
selected_balls.sort()
# Output the selected red balls
for ball in selected_balls:
    print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
# Randomly select 1 blue ball
blue_ball = random.randrange(1, 17)
# Output the selected blue ball
print(f'\033[034m{blue_ball:0>2d}\033[0m')

Note: In the code above, print(f'\033[0m...\033[0m') is used to control the color of the output content, outputting red balls in red and blue balls in blue. The ellipsis represents the content we want to output. \033[0m is a control code indicating to close all attributes, meaning that previous control codes will become invalid. You can also simply understand it as a delimiter. The 0 before m represents the console display mode as the default value. The 0 can be omitted, 1 represents highlight, 5 represents blinking, 7 represents reverse display, etc. Between 0 and m, we can write numbers representing colors, such as 30 for black, 31 for red, 32 for green, 33 for yellow, 34 for blue, etc.

We can also use the sample and choice functions provided by the random module to simplify the code above. The former can implement sampling without replacement, and the latter can randomly select one element. The modified code is as follows.

"""
Double Color Ball random number selection program

Author: Luo Hao
Version: 1.1
"""
import random

red_balls = [i for i in range(1, 34)]
blue_balls = [i for i in range(1, 17)]
# Randomly draw 6 red balls from the red ball list (sampling without replacement)
selected_balls = random.sample(red_balls, 6)
# Sort the selected red balls
selected_balls.sort()
# Output the selected red balls
for ball in selected_balls:
    print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
# Randomly draw 1 blue ball from the blue ball list
blue_ball = random.choice(blue_balls)
# Output the selected blue ball
print(f'\033[034m{blue_ball:0>2d}\033[0m')

If we want to randomly generate N sets of numbers, we just need to place the code above in a loop that repeats N times, as shown below.

"""
Double Color Ball random number selection program

Author: Luo Hao
Version: 1.2
"""
import random

n = int(input('Generate how many sets of numbers: '))
red_balls = [i for i in range(1, 34)]
blue_balls = [i for i in range(1, 17)]
for _ in range(n):
    # Randomly draw 6 red balls from the red ball list (sampling without replacement)
    selected_balls = random.sample(red_balls, 6)
    # Sort the selected red balls
    selected_balls.sort()
    # Output the selected red balls
    for ball in selected_balls:
        print(f'\033[031m{ball:0>2d}\033[0m', end=' ')
    # Randomly draw 1 blue ball from the blue ball list
    blue_ball = random.choice(blue_balls)
    # Output the selected blue ball
    print(f'\033[034m{blue_ball:0>2d}\033[0m')

When we run the code above in PyCharm and input 5, the running effect is shown in the figure below.

lottery_run_result

Here, I’d like to introduce a Python third-party library called rich, which can help us produce the most beautiful output in the simplest way. You can use the Python package management tool pip to install this third-party library in the terminal. For PyCharm users, of course, you should use the pip command in PyCharm’s terminal window to install rich into the project’s virtual environment. The command is as follows.

pip install rich

run_pip_in_terminal

As shown in the figure above, after rich is successfully installed, we can use the code shown below to control the output.

"""
Double Color Ball random number selection program

Author: Luo Hao
Version: 1.3
"""
import random

from rich.console import Console
from rich.table import Table

# Create console
console = Console()

n = int(input('Generate how many sets of numbers: '))
red_balls = [i for i in range(1, 34)]
blue_balls = [i for i in range(1, 17)]

# Create table and add header
table = Table(show_header=True)
for col_name in ('Number', 'Red Balls', 'Blue Ball'):
    table.add_column(col_name, justify='center')

for i in range(n):
    selected_balls = random.sample(red_balls, 6)
    selected_balls.sort()
    blue_ball = random.choice(blue_balls)
    # Add row to table (number, red balls, blue ball)
    table.add_row(
        str(i + 1),
        f'[red]{" ".join([f"{ball:0>2d}" for ball in selected_balls])}[/red]',
        f'[blue]{blue_ball:0>2d}[/blue]'
    )

# Output table through console
console.print(table)

Note: Line 31 in the code above uses list comprehension syntax to process red ball numbers into strings and save them in a list. " ".join([...]) joins multiple strings in the list with spaces into one complete string. If you don’t understand, you can skip it for now. [red]...[/red] in the string is used to set the output color to red, and [blue]...[/blue] on line 32 is used to set the output color to blue. For more information about the rich library, you can refer to the official documentation.

The final output is shown in the figure below. Looking at such output, doesn’t it make you feel a bit better?

output_using_rich

Summary

Python lists are essentially dynamically expandable arrays at the underlying level, with list elements stored contiguously in computer memory, so they can implement random access (obtaining corresponding elements through a valid index, with operation time independent of the number of list elements). We don’t need to touch these underlying storage details for now, nor do you need to understand the asymptotic time complexity of each list method (the relationship between the time spent executing a method and the number of list elements). It’s more important for everyone to first learn to use lists to solve problems at work.