hechengQAQ

导航

 

摘要:在对象的创建和使用期间自动调用。这些特殊方法可以用于实现类似于属性访问、属性删除、属性赋值和属性访问前的特殊行为。

一、__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))
"""

 

posted on 2023-04-07 12:40  hechengQAQ  阅读(21)  评论(0编辑  收藏  举报