为何Python类中部分属性在特定位置无法访问?
Python类体中生成器表达式的名称解析问题
原代码
#!/usr/bin/env python3 ''' Illustration ''' class C1: lst1 = "one", "two", "three" lst2 = "red", "green", "blue" dct1 = { "ten": 10, "eleven": 11, "twelve": 12 } lst3 = tuple(dct1.keys()) lst4 = tuple(x for x in lst2) lst5 = tuple(x for x in lst1 if x not in lst2) # vim:set ft=python ai et ts=4 sts=4 sw=4 cc=80:EOF #
运行报错
Traceback (most recent call last): File "/home/jno/xxx.py", line 7, in <module> class C1: File "/home/jno/xxx.py", line 13, in C1 lst5 = tuple(x for x in lst1 if x not in lst2) File "/home/jno/xxx.py", line 13, in <genexpr> lst5 = tuple(x for x in lst1 if x not in lst2) NameError: name 'lst2' is not defined
疑问
为什么lst2在创建lst4时可被访问,但创建lst5时却无法访问?同一符号在不同语句中的可见性差异是什么?该如何正确引用它?
问题解析与解决
差异原因
- 对于
lst4 = tuple(x for x in lst2):这里的生成器表达式直接在类体作用域内执行,Python会优先在当前类的作用域中查找lst2,而此时lst2已经完成定义,所以能正常访问。 - 对于
lst5 = tuple(x for x in lst1 if x not in lst2):当生成器表达式包含条件判断这类复杂逻辑时,Python会为它创建一个隐式的局部函数作用域。在这个作用域里,Python的名称解析规则是先找局部作用域,再找全局作用域,不会去类的作用域查找名称。所以生成器里的lst1和lst2会被当成全局变量查找,自然找不到,就抛出了NameError。
正确实现方式
你已经找到一种可行方案:通过lambda将类变量作为参数传入,把名称解析逻辑移出生成器的隐式作用域:
class C1: lst1 = "one", "two", "three" lst2 = "red", "green", "blue" dct1 = { "ten": 10, "eleven": 11, "twelve": 12 } lst3 = tuple(dct1.keys()) lst4 = tuple(x for x in lst2) # lst5 = tuple(x for x in lst1 if x not in lst2) lst5 = (lambda P1, P2: tuple(x for x in P1 if x not in P2))(lst1, lst2) # 把名称解析移出"函数作用域"
另外还有一种方式:直接通过类名访问已定义的类属性(类在定义过程中,已经可以用类名访问之前定义的属性):
lst5 = tuple(x for x in C1.lst1 if x not in C1.lst2)
不过这种方式在涉及类继承的场景下可能不符合预期,所以lambda的写法更通用。
内容的提问来源于stack exchange,提问作者jno
相关产品推荐
相关产品推荐

