繁体   English   中英

使用字典理解的Python类变量分配

[英]Python class variable assignment using dictionary comprehension

在类定义期间,定义为字典的类变量用于构造第二个字典类变量,该子变量是从第一个字典中分解而来的子集,如下所示:

class C(object):
    ALL_ITEMS = dict(a='A', b='B', c='C', d='D', e='E')
    SUBSET_X = {k: v for k, v in ALL_ITEMS.items() if k in ('a', 'b', 'd')}  # (this works)
    SUBSET_Y = {k: ALL_ITEMS[k] for k in ('a', 'b', 'd')}  # (this fails)

非常简单的东西,但是执行此代码的最终效果令我感到惊讶。 我的第一种方法是第4行的代码,但我不得不求助于第3行的解决方案。 字典理解范围规则有些微妙的地方,我显然无法掌握。

具体来说,失败情况下引发的错误是:

File "goofy.py", line 4, in <dictcomp>
   SUBSET_Y = {k: ALL_ITEMS.get(k) for k in ('a', 'b', 'd')}
NameError: name 'ALL_ITEMS' is not defined

由于几个不同的原因,此错误的性质使我感到困惑:

  1. SUBSET_Y的赋值是一种格式正确的字典理解,并且引用了应该在范围内且可访问的符号。
  2. 在随后的情况(对SUBSET_X的赋值)中,这也是一种字典理解,符号ALL_ITEMS定义得很好并且可以访问。 因此,在失败的情况下,引发的异常是NameError的事实似乎显然是错误的。 (或至多有误导性。)
  3. 为什么items()__getitem__get()的作用域规则不同? (在失败的情况下,将ALL_ITEMS[k]替换为ALL_ITEMS.get(k)时会发生相同的异常。)

(即使作为Python开发人员已经十多年了,我也从未遇到过这种失败,这意味着我很幸运,或者过着隐蔽的生活:^)

在各种3.6.x CPython版本和2.7.x版本中都会发生相同的故障。

编辑:不,这不是上一个问题的重复。 这与列表理解有关,即使有人对字典理解提出相同的解释,也不能解释我引用的两种情况之间的区别。 而且,这也不是仅限Python 3的现象。

有一个小细节可以解释为什么第一个版本可以工作,而第二个版本却失败。 第二个版本失败的原因与该问题中给出的原因相同,即所有理解构造(在Python 3中,在Python 2中,列表理解的实现方式均不同)创建了一个功能范围,其中所有本地名称绑定发生。 但是,类范围内定义的函数无法访问类范围内的名称。 这就是为什么必须使用self.MY_CLASS_VARMyClass.MY_CLASS_VAR来从方法访问类变量的原因。

您的第一个案例确实起作用的原因很微妙。 根据语言参考

理解由单个表达式组成,后接至少一个for子句和零个或多个for或if子句。 在这种情况下,新容器的元素是通过将每个for或if子句视为一个块,从左向右嵌套,并在每次到达最内层的块时评估表达式以产生一个元素而产生的元素。

但是, 除了最左边的for子句中的可迭代表达式之外,该理解是在单独的隐式嵌套范围内执行的。 这样可以确保在目标列表中分配的名称不会“泄漏”到封闭范围内。

最左边的for子句中的可迭代表达式在封闭范围内直接求值,然后作为参数传递给隐式嵌套范围。

因此,在第一种情况下, ALL_ITEMS.items()位于最左侧的子句中,因此将直接在封闭的范围 (在本例中为类范围)中对其求值 ,因此很高兴地找到ALL_ITEMS名称。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM