为什么列表推导写入循环变量,但生成器不写?

hun*_*nse 76 python list-comprehension generator python-2.7

如果我对列表推导做了些什么,它会写入一个局部变量:

i = 0
test = any([i == 2 for i in xrange(10)])
print i
Run Code Online (Sandbox Code Playgroud)

这打印"9".但是,如果我使用生成器,它不会写入局部变量:

i = 0
test = any(i == 2 for i in xrange(10))
print i
Run Code Online (Sandbox Code Playgroud)

这打印"0".

这种差异有什么好的理由吗?这是设计决策,还是生成器和列表推导的实现方式的随机副产品?就个人而言,如果列表推导没有写入局部变量,那对我来说似乎更好.

pok*_*oke 74

Python的创建者Guido van Rossum在撰写有关Python 3中统一构建的生成器表达式时提及了这一点:(强调我的)

我们还在Python 3中进行了另一项更改,以改进列表推导和生成器表达式之间的等效性.在Python 2中,列表推导将循环控制变量"泄漏"到周围的范围中:

x = 'before'
a = [x for x in 1, 2, 3]
print x # this prints '3', not 'before'
Run Code Online (Sandbox Code Playgroud)

这是列表推导的原始实现的工件; 多年来它一直是Python"肮脏的小秘密"之一.它起初是一种故意的妥协,使列表理解能够快速地进行,虽然它对于初学者来说不是常见的陷阱,但它偶尔会刺激人们.对于生成器表达式,我们不能这样做.生成器表达式使用生成器实现,生成器的执行需要单独的执行帧.因此,生成器表达式(特别是如果它们在短序列上迭代)的效率低于列表推导.

但是,在Python 3中,我们决定使用与生成器表达式相同的实现策略来修复列表推导的"脏小秘密".因此,在Python 3中,上面的例子(修改后使用print(x):-)将打印'before',证明列表理解中的'x'暂时阴影但不覆盖周围的'x'范围.

所以在Python 3中你不会再看到这种情况了.

有趣的是,Python 2中的字典理解也没有这样做; 这主要是因为dict理解是从Python 3向后移植的,因此已经有了修复.

还有一些其他问题也涵盖了这个主题,但我确定你在搜索主题时已经看过那些,对吧?;)

  • @JensTimmerman下面直接的段落包含了这个:*"在你开始担心列表推导在Python 3中变得缓慢之前:由于Python 3中的大量实现工作一般来说加快了速度,所以列表推导和生成器表达式都在Python 3实际上比它们在Python 2中更快!"* (5认同)
  • 这是否意味着与python2相比,python 3中的列表推导速度较慢? (2认同)

Sim*_*ser 16

正如PEP 289(发电机表达式)解释:

循环变量(如果它是简单变量或简单变量的元组)不会暴露给周围的函数.这有利于实现并使典型用例更可靠.

它似乎是出于实施原因而完成的.

就个人而言,如果列表推导没有写入局部变量,那对我来说似乎更好.

PEP 289也澄清了这一点:

列表推导也将其循环变量"泄漏"到周围的范围内.这也将在Python 3.0中发生变化,因此Python 3.0中列表推导的语义定义将等同于list().

换句话说,您描述的行为确实在Python 2中有所不同,但它已在Python 3中修复.


Ign*_*ams 9

就个人而言,如果列表推导没有写入局部变量,那对我来说似乎更好.

你是对的.这在Python 3.x中得到修复.该行为在2.x中保持不变,因此它不会影响(ab)使用此漏洞的现有代码.


归档时间:

查看次数:

2331 次

最近记录:

8 年,12 月 前