我知道在 Python 中可以在运行时向类添加方法:
class Test: def __init__(self): self.a=5 test=Test() import types def foo(self): print self.a test.foo = types.MethodType(foo, test) test.foo() #prints 5
而且我还知道可以覆盖类定义中的默认setattr :
class Test: def __init__(self): self.a=5 def __setattr__(self,name,value): print "Possibility disabled for the sake of this test" test=Test() #prints the message from the custom method called inside __init__
但是,似乎无法在运行时覆盖setattr:
class Test: def __init__(self): self.a=5 test=Test() import types def __setattr__(self,name,value): print "Possibility disabled for the sake of this test" test.__setattr__ = types.MethodType(__setattr__, test) test.a=10 #does the assignment instead of calling the custom method
在最后两种情况下,dir(test) 还报告了方法setattr。但是,虽然在第一种情况下它可以正常工作,但在第二种情况下却不能。请注意,我也可以显式调用它,在这种情况下它可以工作。似乎虽然该方法已定义,但尚未正确映射以覆盖默认分配方法。我遗漏了什么吗?
顺便说一下,我使用的是 Python 2.7。这个问题主要是学术性的,因为从程序设计的角度来看,这样做可能不是一个好主意,但它仍然值得回答——尽管我搜索过,但我没能找到任何地方的文档。
在 Python 中,__setattr__ 是一个特殊方法,用于在对象的属性赋值时拦截并处理属性赋值操作。您所提到的问题实际上涉及了 Python 对于 __setattr__ 方法的处理方式。下面我会解释为什么您的第二种方法没有像预期的那样工作,以及如何正确地覆盖 __setattr__。
__setattr__
Python 中的 __setattr__ 方法并不像普通的方法那样直接在实例对象上进行映射。实际上,__setattr__ 是一个类的特殊方法,当你给实例添加 __setattr__ 时,它并不会直接覆盖默认的属性赋值操作。特别是,__setattr__ 会在类的定义过程中被调用,而不是在运行时动态地通过实例对象调用。
在您的第二个例子中,虽然您尝试通过 test.__setattr__ = types.MethodType(__setattr__, test) 来覆盖 __setattr__,但这个操作不会影响到实例的正常属性赋值机制。Python 会在实例创建时使用类的默认 __setattr__,而不是动态分配的方法。
test.__setattr__ = types.MethodType(__setattr__, test)
__setattr__ 是通过类定义来控制的,它并不是实例方法。因此,当你尝试直接修改实例的 __setattr__ 时,它实际上不会影响类的行为。Python 会始终使用类的 __setattr__ 来处理属性的赋值,而实例本身并不会“接管” __setattr__ 的功能。
要覆盖 __setattr__,应该在类的定义中进行,或者通过 types.MethodType 给类本身动态绑定。换句话说,您不能像普通的实例方法那样修改实例的 __setattr__,而是应该通过修改类的 __setattr__ 来实现。
types.MethodType
class Test: def __init__(self): self.a = 5 def __setattr__(self, name, value): print(f"Setting {name} to {value}") super(Test, self).__setattr__(name, value) # 确保正常设置属性 test = Test() test.a = 10 # 调用自定义的 __setattr__
在这个例子中,我们通过在类定义中定义 __setattr__ 来拦截属性的设置。super() 确保属性可以正常地被设置到实例中。
super()
你可以通过 types.MethodType 来动态地修改类的 __setattr__,而不是修改实例的 __setattr__。这样做会影响整个类的行为:
import types class Test: def __init__(self): self.a = 5 def custom_setattr(self, name, value): print(f"Setting {name} to {value}") super(Test, self).__setattr__(name, value) # 修改类的 __setattr__ 方法 Test.__setattr__ = types.MethodType(custom_setattr, Test) test = Test() test.a = 10 # 调用自定义的 __setattr__