TL:DR概括 -
正如 @CoryKramer 正确指出的,部分原因是字典理解/列表理解/生成器表达式/嵌套函数都在自己的范围内进行评估。但此问题的另一半原因是由于使用eval()
,它在调用它的环境中执行其表达式,但它无权访问封闭的名称空间。
另外,我相信你不应该使用eval()
(这很危险)。用于获取属性self
,你应该使用getattr()功能 -
data = {element:getattr(self,element) for element in key}
对于那些感兴趣的人,我对这个问题的发现 -
部分原因是字典理解/列表理解/生成器表达式/嵌套函数都在自己的范围内进行评估。但此问题的另一半原因是由于使用eval()
.
正如文档中给出的eval() -
eval(expression[, globals[, locals]])
如果两个字典都被省略,则表达式将在调用 eval() 的环境中执行。返回值是计算表达式的结果。语法错误被报告为异常。
(强调我的)
通常在课堂上,当您使用字典理解时,您可以使用self
等等在字典理解中。例子 -
>>> class CA:
... def __init__(self):
... self.a = "Hello"
... print({k:self.a for k in range(2)})
...
>>> CA()
{0: 'Hello', 1: 'Hello'}
<__main__.CA object at 0x008B22D0>
如您所见,可以访问self.a
在字典理解范围内。现在让我们检查一下是什么locals()
(本地命名空间)用于字典理解 -
... #same as above, just change the print function call.
print({k:locals() if k < 2 else self.a for k in range(2)})
Result -
{0: {'.0': <range_iterator object at 0x02373998>, 'self': <__main__.CA object at 0x008B22D0>, 'k': 1},
1: {'.0': <range_iterator object at 0x02373998>, 'self': <__main__.CA object at 0x008B22D0>, 'k': 1}}
可以看出'self'
可以在字典理解中访问(因为它是自由变量,这只是因为我使用了self.a
直接在字典理解中,如果我没有添加它在那里就不是一个自由变量。让我们解释一下自由变量 a bit -
如果名称绑定在块中,则它是该块的局部变量。如果名称绑定在模块级别,则它是全局变量。 (模块代码块的变量有局部变量和全局变量。)如果一个变量在代码块中使用但未在代码块中定义,则它是一个变量自由变量.
但是当你使用eval()
为了执行表达式,Python 不知道在表达式内部使用的任何名称(事先,在执行表达式之前)eval()
,因此它不能绑定self
作为字典理解的自由变量。打印示例locals()
,当使用eval
to get self.a
-
...
print({k:locals() if k < 2 else eval('self.a') for k in range(2)})
Result -
{0: {'.0': <range_iterator object at 0x023739B0>, 'k': 1}, 1: {'.0': <range_iterator object at 0x023739B0>, 'k': 1}}
因此,当在 eval 内计算表达式时,它没有self
在执行环境中定义的变量。如果你要使用self
在字典理解中的任何地方,您都不会出现此错误 -
...
print({k:eval('self.a') if k < 2 else self for k in range(2)})
Result -
{0: 'Hello', 1: 'Hello'}
因为那时的环境eval
正在执行的表达式知道名称绑定self
.
也可以使用嵌套函数来复制完全相同的问题 -
>>> def a():
... localb = 10
... def c():
... print(locals())
... print(eval('localb + 20'))
... c()
...
>>> a()
{}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in a
File "<stdin>", line 5, in c
File "<string>", line 1, in <module>
NameError: name 'localb' is not defined