![]() |
1
190
类范围和列表、集合或字典理解以及生成器表达式不混合。 原因;或者,官方的说法在Python3中,列表理解被赋予了一个适当的作用域(本地名称空间),以防止它们的本地变量溢出到周围的作用域中(请参见 Python list comprehension rebind names even after scope of comprehension. Is this right? )。在模块或函数中使用这样的列表理解非常好,但是在类中,范围界定有点,嗯, 奇怪的 . 这记录在 pep 227 :
而在
重点挖掘;执行框架是临时范围。
因为作用域被重新用作类对象的属性,允许它用作非本地作用域也会导致未定义的行为;如果类方法引用
最后,但绝对不是最不重要的是 Naming and binding 执行模型文档中的部分明确提到了类作用域:
因此,总结一下:不能从包含在该作用域中的函数、列表理解或生成器表达式访问类作用域;它们的作用就像该作用域不存在一样。在Python2中,列表理解是使用快捷方式实现的,但在Python3中,它们有自己的函数作用域(它们应该一直都有),因此示例中断。不管python版本如何,其他理解类型都有自己的作用域,因此在python 2中会出现一个带有set或dict理解的类似示例。
(小)例外;或者,为什么一部分 可以 仍然工作
理解或生成器表达式的一部分在周围的范围内执行,而不管python版本如何。这将是最外层iterable的表达式。在你的例子中,是
因此,使用
这只适用于最外层的iterable;如果一个理解有多个
这个设计决策是为了在genexp创建时抛出错误,而不是在创建生成器表达式的最外层iterable时抛出错误,或者在最外层iterable结果不是iterable时抛出错误。理解共享这种行为以保持一致性。 从引擎盖下面看;或者,比你想要的要详细得多
您可以使用
到
创造
作为一个类,python基本上采用了构成类体的整个套件(因此所有的东西都比
第一
这里要注意的是,字节码包含一个嵌套的代码对象;在python中,类定义、函数、理解和生成器都表示为代码对象,这些对象不仅包含字节码,还包含表示局部变量、常量、从g中获取的变量的结构。从嵌套作用域获取的lobals和变量。编译的字节码引用那些结构,而python解释器知道如何访问给定字节码的那些结构。
这里要记住的重要一点是,python在编译时创建这些结构;
让我们检查创建类主体本身的代码对象;代码对象有
上面的字节码创建类体。函数被执行,结果是
从中可以看出,函数或生成器的代码对象与理解的代码对象之间的唯一区别是后者是执行的 立即 当父代码对象被执行时;字节码只是动态地创建一个函数,然后用几个小步骤执行它。 python 2.x在那里使用内联字节码,这里是python2.7的输出:
没有加载代码对象,而是
然而,当解释器第一次加载模块或脚本时,理解是与python源代码的其余部分一起编译的,而编译器确实
不
将类套件视为有效范围。列表理解中的任何引用变量都必须在作用域中查找
周围的
类定义,递归地。如果编译器找不到该变量,则将其标记为全局变量。对list comprehension code对象的反汇编表明
这个字节码块加载传入的第一个参数
我们确定了吗
这个
实际引用从当前帧数据结构中查找值,这些数据结构是从函数对象的
所以,总结一下:
解决办法;或者,该怎么办
如果要为
“临时的”
当然,阅读您的代码的人会对此略知一二;您可能想在其中加上一个很大的注释来解释为什么要这样做。
最好的办法就是
避免所有的挠头和问题来解释你自己。对于你自己的具体例子,我甚至不会存储
|
![]() |
2
12
在我看来,这是python 3中的一个缺陷。我希望他们能改变。
旧方法(在2.7中工作,抛出
注:仅用
新方法(适用于3+):
因为语法太难看了,所以我只需在构造函数中初始化所有类变量 |
![]() |
3
5
公认的答案提供了极好的信息,但这里似乎还有一些其他的问题——列表理解和生成器表达式之间的差异。我玩过的演示:
|
![]() |
4
1
这是python中的一个bug。理解被宣传为等同于for循环,但这在类中是不正确的。至少在Python3.6.6之前,在类中使用的理解中,只有一个来自理解外部的变量可以在理解内部访问,并且它必须用作最外层的迭代器。在函数中,此范围限制不适用。 为了说明为什么这是一个bug,让我们回到原来的示例。失败了:
但这是有效的:
限制在 this section 在参考指南中。 |
![]() |
5
0
因为最外层的迭代器是在周围的作用域中计算的,所以我们可以使用
也可以使用嵌套
对于op的具体示例:
|