Python List Comprehensions Tutorial

1. Basic concept: a line of code replaces the "magic sugar" of a loop

When writing Python, generating lists is the most common operation - from counting square numbers, filtering data to processing files, lists are used everywhere.

When newbies get started, they will most likely write in the loop+append method first. For example, to generate a square list of 1-10:

# 新手常用写法
squares = []
for x in range(1, 11):
    squares.append(x**2)
print(squares)  # 输出: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

But if you use list generation, you can do it in one line:

# 列表生成式写法
squares = [x**2 for x in range(1, 11)]
print(squares)  # 输出同上

This is the charm of list generation: it is Python's built-in "syntactic sugar", which can compress the logic of "traverse iterable objects → convert elements → add new lists" into one line, which is both concise and clear (provided that it is not too complicated 😅).


2. Basic syntax breakdown

The core structure of list generation is very intuitive, like writing the circular logic "inside out":

[对元素做的操作 for 变量 in 可迭代对象]

For example, the square example just now:

  • range(1, 11): Iterable object, providing numbers from 1 to 10
  • x: Loop variable, receiving elements of the iterable object one by one
  • x**2: for eachxoperation, calculate the square
  • []: Finally, wrap all results into a list

3. Advanced: Conditional list generation

List generation can not only do "conversion", but can also add filtering and more flexible conversion rules.

3.1 Filter condition: inforadd laterif

If you only want to keep the squares of even numbers, you don’t need to write it in the loopifMake a judgment and then append, just add the filter conditions at the end:

even_squares = [x**2 for x in range(1, 11) if x % 2 == 0]
print(even_squares)  # 输出: [4, 16, 36, 64, 100]

⚠️ Key points to note: HereifIt is a filter, only leaving elements that meet the conditions, cannot be addedelse——Because the non-compliant ones are discarded directly, no substitute value is needed.


3.2 Conditional expression: inforAdd beforeif...else

If you need to perform a "choose one" conversion on each element (for example, keep even numbers and turn odd numbers into negative numbers), you must use Python's ternary conditional expression, and it must be placed inforin front of:

# 偶数留原值,奇数取负
numbers = [x if x % 2 == 0 else -x for x in range(1, 11)]
print(numbers)  # 输出: [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]

⚠️ Key points to note: Hereif...elseis part of the converter and must be complete (withelse) - Because each element must generate a value and cannot be discarded.


4. Multi-level loops: processing nested iterable objects

List generation expressions can also be used to write double, triple or even more levels of loops to generate nested structures or full permutations and combinations.

4.1 Double loop: generate full permutation

# 遍历字母'A'/'B'/'C'和'X'/'Y'/'Z',两两拼接
combinations = [m + n for m in 'ABC' for n in 'XYZ']
print(combinations)  # 输出: ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

loop order and you write nestedforThe order is consistent: execute outer layer firstfor m in 'ABC', and then execute the inner layerfor n in 'XYZ', equivalent to:

combinations = []
for m in 'ABC':
    for n in 'XYZ':
        combinations.append(m + n)

4.2 Triple loop: generate simple three-dimensional coordinates

The logic of triple and above loops will start to become complicated. Unless it is very simple, it is not recommended to use list generation - the readability will be reduced. For example, generate a 2x2x2 cube coordinate point:

points = [(x, y, z) for x in range(2) for y in range(2) for z in range(2)]
print(points)  # 输出: [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)]

5. Small case of actual scenario

Just talk without practicing, let’s see how list generation is used in daily development:

5.1 Processing dictionaries: formatting key-value pairs

Traverse the dictionaryitems(), get the key and value at the same time:

d = {'x': 'A', 'y': 'B', 'z': 'C'}
formatted_pairs = [f"{k}={v}" for k, v in d.items()]
print(formatted_pairs)  # 输出: ['x=A', 'y=B', 'z=C']

5.2 Simple file system operation: filtering .py files

combineosModule, lists all Python scripts in the current directory:

import os

# 先列所有文件/目录,再筛选后缀为.py的
py_scripts = [f for f in os.listdir('.') if f.endswith('.py')]
print(py_scripts)

5.3 Type safety processing: only handle strings

When there are mixed types (such as strings, numbers, None) in the list, useisinstance()Do a type check first to avoid calling non-existent methods and reporting errors:

mixed_list = ['Hello', 'World', 18, 'Apple', None]
# 只把字符串转小写,其他类型直接丢弃
safe_lower = [s.lower() for s in mixed_list if isinstance(s, str)]
print(safe_lower)  # 输出: ['hello', 'world', 'apple']

6. Performance and memory tips

6.1 List generation vs traditional loop: who is faster?

Typically, list generation is more efficient than traditionalfor+appendFaster - because the Python interpreter can directly optimize the underlying execution of list generation, reducing the number of intermediate bytecode calls.


6.2 What to do with large amounts of data? Use generator expressions!

If you need to process hundreds of thousands or even millions of elements, list generation will load all the results into memory at once, which may cause memory overflow.

At this time, you can[]Replace with(), becomes a generator expression: it will not generate all elements immediately, but when traversing (such asforloop) before "spitting it out" one by one, greatly saving memory:

# 生成器表达式(注意用小括号)
big_gen = (x**2 for x in range(1000000))

# 遍历时才计算
for num in big_gen:
    if num > 100:
        break  # 可以提前终止,不用算完所有

7. Python 3.8+ small extension: walrus operator

The walrus operator introduced in Python 3.8:=, you can calculate the value first in the list generation and then determine whether to retain it to avoid repeated calculations:

# 需求:计算1-10的平方,只保留大于50的
# 传统写法:x**2会算两次(一次在if,一次在结果)
result_old = [x**2 for x in range(1, 11) if x**2 > 50]

# 海象写法:x**2算一次,赋值给square,同时判断
result_new = [square for x in range(1, 11) if (square := x**2) > 50]
print(result_new)  # 输出: [64, 81, 100]

8. Small exercises (with answers)

Try writing it yourself: filter listL1 = ['Hello', 'World', 18, 'Apple', None], only keep the string and convert it to lower case, and finally print the result.

Click to view the answer
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [s.lower() for s in L1 if isinstance(s, str)]

# 简单测试
print(L2)
if L2 == ['hello', 'world', 'apple']:
    print('测试通过!🎉')
else:
    print('测试失败😅')

9. Summary: When to use list generation?

List generation is a good thing, but don't abuse it! Recommended for the following scenarios:

  1. Simple "Traverse→Convert→Filter" logic (can be written clearly in one line)
  2. Small scripts that require code simplicity

If there are complex nested judgments or multi-line conversion logic in the loop, it is better to use the traditionalforLoop + comment** - readability is always the first priority👍.