对Python中getattr、setattr、delattr字节码编译限制的疑问
你测试里的setattr(cl, 'var', 2)能成功,是因为你显式引用了类对象(通过全局变量cl),这完全符合Python规则,并没有触发文档中提到的限制场景。
文档里的“字节码编译限制”核心是:传递给exec/eval的代码,无法像与类一起编译的原生代码那样,隐式地将调用它的类视为当前类的命名空间上下文。具体来说:
1. 类定义阶段的限制场景
当你在类的定义体内部使用exec时,exec的代码无法像原生类代码那样,直接将变量绑定为类属性——除非你显式操作类对象。比如:
class MyClass: # 原生代码:直接将var绑定为类属性 native_var = 1 # exec代码:这里的exec_var只是类定义阶段的临时变量,不会成为MyClass的属性 exec("exec_var = 2") print(MyClass.native_var) # 输出1 print(hasattr(MyClass, 'exec_var')) # 输出False
如果要在exec里给类添加属性,必须显式用setattr并指定类对象(此时可通过__class__获取未完全创建的类):
class MyClass: exec("setattr(__class__, 'exec_var', 2)") print(MyClass.exec_var) # 输出2
2. 类方法中的限制场景
在类的方法内部,exec的代码无法像原生方法代码那样,自动识别当前类的上下文。比如,你可能以为在exec里直接写var = 2会修改类属性,但实际上只会创建方法的局部变量:
class MyClass: var = 0 def func(self): # 原生代码:直接修改类属性 MyClass.var = 1 # exec代码:这里的var是方法局部变量,和类属性无关 exec("var = 2") print(f"局部var:{var}") print(f"类属性var:{MyClass.var}") MyClass().func() # 输出: # 局部var:2 # 类属性var:1
而如果你在exec里用setattr但没有显式指定类对象,试图依赖当前命名空间隐式操作类,同样不会生效;只有显式指定__class__或类名,才能正确修改类属性。
3. 和global关键字限制的关联
你测试的global例子里,exec内的global x无法影响外部函数的局部变量,本质是因为exec的代码是独立编译的,无法修改原函数字节码中变量的作用域标记。而setattr等函数的限制逻辑类似:exec的代码无法共享原类/方法的编译时上下文,必须显式指定操作的对象,不能依赖隐式的当前类命名空间。
总结来说,文档中的限制不是说不能在exec里用setattr,而是说不能依赖类的编译时上下文隐式操作类属性,必须显式指定要操作的类对象——这也是你的测试代码能成功的原因,因为你明确传递了cl这个类对象给setattr。
内容的提问来源于stack exchange,提问作者Dhruv

