Contents
30.4.2. 推导逻辑的子表达式不要超过两个¶
除了最基本的用法外,列表推导式还支持多层循环。例如把矩阵转化成普通的一维列表,那么可以在推导时,使用两条for子表达式。这些子表达式会按照从左到右的顺序解读。
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [x for row in matrix for x in row]
print(flat)
"""
[1, 2, 3, 4, 5, 6, 7, 8, 9]
"""
多层循环还可以用来重制那种两层深的结构。例如,如果要根据二维矩阵里每个元素的平方值构建一个新的二维矩阵,那么可以采用下面的写法。
squared = [[x ** 2 for x in row] for row in matrix]
print(squared)
'''
[[1, 4, 9], [16, 25, 36], [49, 64, 81]]
'''
这看上去有点复杂,因为它把小的推导式逻辑[x**2 for x in row]嵌到了大的推导逻辑里面。大的推导逻辑用来决定新矩阵里的每一行,小的推导逻辑决定行中的每个元素。
my_lists = [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]], ]
flat = [x for sublist1 in my_lists for sublist2 in sublist1 for x in sublist2]
print(flat)
'''
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
'''
此时,采用列表推导式来实现,其实并不会比传统的for循环节省多少代码。下面用for循环来写一次,这要比刚才那种三层矩阵的列表推导式更加清晰。
flat = []
for sublist1 in my_lists:
for sublist2 in sublist1:
flat.extend(sublist2)
print(flat)
'''
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
'''
推导的时候,可以使用多个if条件。如果这些if条件出现在同一层循环内,那么它们之间默认是and关系。
例如,如果要用原列表中大于4且是偶数的值来构建新列表,那么既可以连用两个if,也可以只用一个if。
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
b = [x for x in a if x > 4 if x % 2 == 0]
c = [x for x in a if x > 4 and x % 2 == 0]
print(b)
print(c)
assert b and c
assert b == c
'''
[6, 8, 10]
[6, 8, 10]
'''
在推导时,每一层的for子表达式都可以带有if条件。例如,要根据原矩阵构建新的矩阵,把其中各元素之和大于等于10的那些行选出来,而且只保留其中能够被3整除的那些元素。
这个逻辑用列表推导来写,并不需要太多的代码,但是这些代码理解起来会很困难。
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
filtered = [[x for x in row if x % 3 ==0]
for row in matrix if sum(row) >= 10]
print(filtered)
'''
[[6], [9]]
'''
总之,在表示推导逻辑时,最多只应该写两个子表达式(例如两个if条件、两个for循环,或者一个if条件与一个for循环)。
只要实现的逻辑比这还复杂,那就应该采用普通的if与for语句来实现,并且可以考虑编写辅助函数
要点:
推导的时候可以使用多层循环,每层循环可以带有多个条件。
控制推导逻辑的子表达式不要超过两个,否则代码很难读懂。