列表推导式(list comprehensions)是一种基于现有可迭代对象使用简洁语法创建列表的简便方法:
squares = [x**2 for x in range(10)]
这段代码等价于:
squares = [] for x in range(10): squares.append(x**2)
列表推导式有几个优点:
evens = [x for x in range(10) if x % 2 == 0];与map()的类似示例:
def f(x): return x**2 squares = list(map(f, range(10)))
map在大数据情况下更快,如果使用的是已经存在于C中的函数,并且适合将一个函数应用于所有元素。列表推导式适用于任何表达式,而不仅仅是现有函数。for循环更灵活,但显得笨重。
为什么在
[x for x in range(10)]这样的表达式中,变量x在Python2执行后仍然可在表达式外部访问,而在Python3中则不可?
答案: 在Python2中,循环变量(x)在执行列表推导式后保留其值。而在Python3中,它被“隔离”,在列表外部不可访问,因此防止了不必要的副作用。
示例:
# Python 2.x: [x for x in range(3)] print(x) # x == 2 # Python 3.x: [x for x in range(3)] print(x) # NameError: name 'x' is not defined
故事 1
一位开发者在一个大型项目中想通过列表推导式过滤并创建一个新列表:
my_list = [item.transform() for item in data if item.is_valid()]
但是,如果item.is_valid()返回False,item.transform()的操作会引发错误。然而,检查函数编写时有潜在的副作用,最终列表推导式在不明显的情况下破坏了具有副作用的代码部分。
故事 2
在项目从Python2迁移到Python3时,开发者确信循环变量会保持可用性:
[x for x in range(5)] print(x) # 预计得到4,但收到NameError。
这导致了循环逻辑中的一个bug,变量必须在列表推导式之外保持有效。
故事 3
使用嵌套列表推导式而没有明确指定级别:
def flatten(matrix): return [cell for row in matrix for cell in row]
新手经常因为遍历顺序错误而出现错误(例如[cell for cell in row for row in matrix]或多余的嵌套),导致结果错误——一维列表而不是二维列表,反之亦然。