如何在无类的当前脚本中重写魔术方法?__setattr__未触发排查
1. 不使用类时,为当前脚本重写魔术方法
咱们得先明确:Python里大部分魔术方法是和类/实例绑定的,但脚本(模块)层面也能处理部分魔术方法,分两种情况来说:
模块原生支持直接定义的魔术方法:比如Python 3.7+之后,模块里可以直接定义
__getattr__和__dir__。当你访问模块里不存在的属性时,__getattr__会自动触发,举个例子:def __getattr__(name): return f"嘿,当前模块没有 {name} 这个属性哦~" print(nonexist_attr) # 会调用__getattr__,输出对应的字符串需要手动绑定到模块对象的魔术方法:像
__setattr__这种,模块类默认没实现,所以得先拿到当前模块的对象,再替换它的__setattr__方法。比如:import sys # 获取当前脚本对应的模块对象 current_mod = sys.modules[__name__] def custom_setattr(name, value): print(f"正在给模块属性 {name} 赋值:{value}") # 调用object的__setattr__来完成实际赋值,避免递归 object.__setattr__(current_mod, name, value) # 把自定义方法绑定到模块对象上 current_mod.__setattr__ = custom_setattr # 现在赋值模块变量就会触发自定义逻辑了 b = 10 # 会打印"正在给模块属性 b 赋值:10"
要注意的是,修改模块的魔术方法只对当前脚本有效,而且尽量避免过度使用,不然容易让代码可读性下降。
2. 赋值变量b时未触发__setattr__,你可能遗漏了这些点
__setattr__的触发是有明确场景的,没触发大概率是你没满足它的调用条件,常见的几个遗漏点:
场景1:你在模块级别赋值,模块默认没有__setattr__
如果b是模块级变量(也就是直接在脚本里写b = xxx),默认不会触发__setattr__——因为Python的模块对象默认没实现这个方法。解决办法就是上面第一个问题里的操作:给当前模块对象绑定自定义的__setattr__。
场景2:在类实例中直接操作了__dict__
如果你是在类里写代码,比如直接通过self.__dict__['b'] = xxx来赋值,这会绕过__setattr__方法,自然不会触发。正确的写法应该是用self.b = xxx,比如:
class Test: def __setattr__(self, name, value): print(f"触发__setattr__:{name} = {value}") super().__setattr__(name, value) obj = Test() obj.b = 20 # 会触发__setattr__ obj.__dict__['b'] = 30 # 不会触发,因为直接操作了属性字典
场景3:你给类属性赋值,而非实例属性
__setattr__分实例方法和类方法两种:如果你是给类本身的属性赋值(比如Test.b = xxx),触发的是类的__setattr__,而不是实例的。只有给实例对象的属性赋值时,才会触发实例的__setattr__:
class Test: def __setattr__(self, name, value): print(f"实例__setattr__触发:{name}") super().__setattr__(name, value) Test.b = 10 # 不会触发实例的__setattr__ obj = Test() obj.b = 20 # 会触发实例的__setattr__
你可以对照自己的代码,看看是哪种情况导致的~
内容的提问来源于stack exchange,提问作者Nae

