摘要:在对象的创建和使用期间自动调用。这些特殊方法可以用于实现类似于属性访问、属性删除、属性赋值和属性访问前的特殊行为。
一、__setattr__
用于在属性赋值时被自动调用,并将该属性名和属性值存储在实例字典中。该方法可以用来检查属性值的合法性,也可以用来实现属性赋值时的复杂逻辑
class MyClass: def __setattr__(self, name, value): print(f"Setting {name} to {value}") # 可写一些规范属性等其他逻辑 super().__setattr__(name, value) # 我们使用super()方法调用父类的__setattr__方法,以便在实例字典中存储属性 obj = MyClass() obj.x = 5 """ 执行结果: Setting x to 5 """
class SuperDict(dict): def __getattr__(self, item): return self[item] def __setattr__(self, key, value): # obj.x = 5 self[key] = value def to_json_str(self): return json.dumps(self)
二、__getattr__
当访问一个不存在的属性时自动调用该方法。该方法可以用来实现默认属性值或者在属性不存在时执行其他操作。
class Person: def __init__(self, name): self.name = name def __getattr__(self, name): if name == 'age': # 设置默认属性 return 18 raise AttributeError(f"'Person' object has no attribute '{name}'") p = Person("Alice") print(p.age) # will print 18 print(p.address) # will raise an AttributeError
print(p.name) # 不会调用__getattr__
三、__delattr__
当一个属性被删除时自动调用该方法。该方法可以用来检查是否允许删除该属性,或者在删除属性时执行其他清理逻辑。
class Person: def __init__(self, name, age): self.name = name self.age = age def __delattr__(self, name): if name == 'age': raise AttributeError("Cannot delete age attribute") super().__delattr__(name) p = Person("Alice", 25) del p.age # will raise an AttributeError 将该属性名从实例字典中删除,但抛错,没有删除掉
四、__getattribute__
当访问一个属性时自动调用该方法。该方法可以用来实现高级属性访问控制,例如权限检查、日志记录等。
class Person: def __init__(self, name): self._name = name self._age = 18 def __getattribute__(self, name): if name.startswith('_'): raise AttributeError(f"'Person' object has no attribute '{name}'") return super().__getattribute__(name) p = Person("Alice") print(p.name) # will print "Alice" print(p._age) # will raise an AttributeError
__getattr__与__getattribute__区别:
__getattr__是访问不存在的对象属性时被自动调用,更适合用于处理对象不存在的属性的情况。
__getattribute__是访问对象属性前被自动调用,无论对象的属性是否存在,若访问不存在的对象属性,它先于__getattr__被调用,更适合用于拦截对所有属性的访问并执行自定义操作的情况。
五、注意点
__getattribute__方法可能会导致无限递归问题,因此需要小心使用:
class MyClass: def __getattr__(self, name): print(f"Getting {name}") print(self.__dict__) return 42 def __getattribute__(self, item): raise AttributeError("error") obj = MyClass() obj.b = 5 print(obj.x) """ 这段代码引发无限递归问题,访问obj.x调用__getattribute__ 抛出异常表示不存在属性,执行__getattr__,执行到self.__dict__表示属性访问,触发执行__getattribute__,进入了无限递归循环 """当重载__getattr__、__getattribute__时:
class MyClass: def __getattr__(self, name): print(f"Getting {name}") print(self.__dict__) return 42 def __getattribute__(self, item): print("我被打印") obj = MyClass() obj.b = 5 print(obj.x) """ 执行这段代码,__getattr__不会被调用,即使访问了不存在的属性,优先执行了__getattribute__方法,不执行任何操作,所以它会屏蔽掉__getattr__的调用。自定义时访问不存在的属性调用__getattribute__要抛出异常,表示属性不存在,才能调用__getattr__,否则不会调用。而且默认是调用父类的__getattribute__(super().__getattribute__(item)) """