Python类内外同名变量作用域差异:生成式与直接取值为何结果不同?
类内与全局同名变量的优先级差异解析
这是个挺有意思的Python作用域细节问题,咱们先把两个示例摆出来,再拆解背后的原因:
示例1:直接使用类内变量
a = 0 b = 1 class A: a = 42 b = list((a, a+1, a+2)) x = A() print(x.b)
输出:
[42, 43, 44]
示例2:意外使用全局变量
a = 0 b = 1 class A: a = 42 b = list((a+i for i in range(3))) x = A() print(x.b)
输出:
[0, 1, 2]
核心差异原因
这俩例子的区别,本质是类定义阶段的作用域规则和生成器表达式的延迟绑定特性共同作用的结果:
对于示例1:
list((a, a+1, a+2))里的a是在类定义的过程中直接求值的。Python在创建类的时候,会临时生成一个类专属的作用域,此时类内已经定义了a=42,所以这里的a直接引用类作用域中的变量,生成对应的列表。对于示例2:
(a+i for i in range(3))是一个生成器表达式,它最关键的特性是延迟求值——里面的变量不会在定义时立刻解析,而是等到被list()触发迭代的时候才会去查找变量。当list()开始迭代这个生成器时,类定义已经完成,此时生成器会按照Python的作用域查找链:先找自身局部作用域,找不到就找全局作用域,但不会去查找类的作用域(类作用域对生成器来说既不是局部也不是全局),所以最终找到了全局的a=0。
如果想让示例2也使用类内的a,可以提前把类内的a赋值给一个局部变量,或者换成列表推导式(Python3中列表推导式会在定义时捕获类作用域的变量),比如把生成器改成[a+i for i in range(3)],输出就会和示例1一致了。
内容的提问来源于stack exchange,提问作者Qingwan Kuah
相关产品推荐
相关产品推荐

