使用字典理解的Python类变量分配 [英] Python class variable assignment using dictionary comprehension

查看:49
本文介绍了使用字典理解的Python类变量分配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

During a class definition, a class variable defined as a dictionary is used in the construction of a second dictionary class variable, a subset pared down from the first, like this:

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行的解决方案.字典理解范围规则有些微妙的地方,我显然无法掌握.

Pretty simple stuff, but the net effect of executing this code is quite surprising to me. My first approach was the code on line 4, but I had to resort to the solution on line 3 instead. There is something subtle about dictionary comprehension scoping rules that I'm clearly failing to grasp.

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

Specifically, the error raised in the failing case is:

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

这个错误的性质由于一些不同的原因而使我感到困惑:

The nature of this error is baffling to me for a few different reasons:

  1. SUBSET_Y 的赋值是一种格式正确的字典理解,并且引用了应该在范围内且可访问的符号.
  2. 在随后的情况(对 SUBSET_X 的赋值)中,这也是一个字典理解,符号 ALL_ITEMS 定义得很好并且可以访问.因此,在失败的情况下,引发的异常是 NameError 的事实似乎显然是错误的.(或至多是误导.)
  3. 为什么 items() __ getitem __ get()的范围规则不同?(在失败的情况下,将 ALL_ITEMS [k] 替换为 ALL_ITEMS.get(k)会发生同样的异常.)
  1. The assignment to SUBSET_Y is a well-formed dictionary comprehension, and references a symbol which should be in-scope and accessible.
  2. In the succeeding case (the assignment to SUBSET_X), which is also a dictionary comprehension, the symbol ALL_ITEMS is perfectly well-defined and accessible. Thus, the fact that the raised exception is a NameError in the failing case seems manifestly wrong. (Or misleading, at best.)
  3. Why would the scoping rules differ for items() vs. __getitem__ or get()? (The same exception occurs replacing ALL_ITEMS[k] with ALL_ITEMS.get(k) in the failure case.)

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

(Even as a Python developer for over a decade, I've never run into this failure before, which either means I've been lucky or have lived a sheltered existence :^)

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

The same failure occurs in various 3.6.x CPython versions as well as 2.7.x versions.

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

No, this is not a duplicate of a previous question. That pertained to list comprehensions, and even if one were to project the same explanation to dictionary comprehensions, it doesn't explain the difference between the two cases I cited. And also, it is not a Python 3-only phenomenon.

推荐答案

有一个小细节可以解释为什么第一个版本可以工作,而第二个版本却失败.第二个版本失败的原因与

There is one minor detail that explains why the first-version works but the second version fails. The reason the second version fails is the same reason that is given in this question, namely, all comprehension constructs (in Python 3, in Python 2, list-comprehensions were implemented differently) create a function scope where all of the local name-bindings occur. However, names in a class scope are not accessible to functions defined inside the class scope. This is why you have to use either self.MY_CLASS_VAR or MyClass.MY_CLASS_VAR to access a class variable from a method.

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

The reason your first case does happen to work is subtle. According to the language reference

理解力由单个表达式组成,后接至少一个for子句,零个或多个for或if子句.在这种情况下,新容器的元素是那些将由将每个for或if子句视为一个块,从左侧嵌套向右,然后评估表达式以分别产生一个元素到达最里面的块的时间.

The comprehension consists of a single expression followed by at least one for clause and zero or more for or if clauses. In this case, the elements of the new container are those that would be produced by considering each of the for or if clauses a block, nesting from left to right, and evaluating the expression to produce an element each time the innermost block is reached.

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

However, aside from the iterable expression in the leftmost for clause, the comprehension is executed in a separate implicitly nested scope. This ensures that names assigned to in the target list don’t "leak" into the enclosing scope.

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

因此,在第一种情况下, ALL_ITEMS.items()位于最左侧的子句中,因此直接在封闭范围内对其进行评估,在在这种情况下,它是类作用域,因此它很高兴地找到 ALL_ITEMS 名称.

So, in the first case, ALL_ITEMS.items() is in the left-most for-clause, so it is evaluated directly in the enclosing scope, in this case, the class scope, so it happily finds the ALL_ITEMS name.

这篇关于使用字典理解的Python类变量分配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆