Python自学成才之路 魔术方法之属性访问控制

时间:2022-07-23
本文章向大家介绍Python自学成才之路 魔术方法之属性访问控制,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Python中提供了一些魔术方法来控制对象属性的访问,赋值,删除过程。

属性访问魔术方法

__getattr__(self, item)
__getattribute__(self, item)

其中__getattr__只有在属性不存在时会被调用,__getattribute__无论属性是否存在都会被调用,item参数就是要访问的属性。

属性赋值魔术方法

__setattr__(self, key, value)

给对象属性赋值或者添加新属性时会被调用。

属性删除魔术方法

__delattr__(self, item)

当删除一个对象属性时,该方法会被调用。

下面通过一个案例来展示上面三个魔术方法的用法,其中age属性的值通过birth_date元素来计算出来的。

class Person(object):

    def __init__(self, name: str, birth_date: int):
        self.name = name
        self.birth_date = birth_date
        self.age = 0

    def __getattr__(self, item):
        raise AttributeError(item + "属性不存在")

    def __getattribute__(self, item):
        print("getattribute: %s" %item)
        return super(Person, self).__getattribute__(item)

    def __setattr__(self, key, value):
        print('__setattr__, key = %s, value = %s'%(key,value))
        if key == 'birth_date':
            super(Person, self).__setattr__('birth_date', value)
            super(Person, self).__setattr__('age', 2020 - value)
        else:
            # 必须加上这一步 否则所有的属性添加都会失败
            super(Person, self).__setattr__(key, value)

    def __delattr__(self, item):
        super(Person, self).__delattr__(item)
        print('delattr: %s'%item)


p1 = Person('peter', 2010)
print(p1.name)
print(p1.age)
p1.gender = 'man'
del p1.age
# 保证异常打印顺序
time.sleep(1)
print(p1.age)

输出:
__setattr__, key = name, value = peter
__setattr__, key = birth_date, value = 2010
__setattr__, key = age, value = 0
getattribute: name
peter
getattribute: age
0
__setattr__, key = gender, value = man
delattr: age
getattribute: age
Traceback (most recent call last):
  File "D:/pycharm workspace/oopdemo/magic_method/AttrDemo.py", line 89, in <module>
    print(p1.age)
  File "D:/pycharm workspace/oopdemo/magic_method/AttrDemo.py", line 61, in __getattr__
    raise AttributeError(item + "属性不存在")
AttributeError: age属性不存在

案例中__setattr__方法控制添加属性和给属性赋值的过程,通过birth_date属性来计算出age属性的值。

在使用这些访问控制魔术方法需要注意一点,不能通过self.xxx(备注:这里指的是访问控制魔术方法)的方式来访问,这样可能会导致死循环。比如把案例中的__getattribute__方法改成下面这样:

def __getattribute__(self, item):
    print("getattribute: %s" %item)
    return self.__getattribute__(item)

执行后会抛出RecursionError异常。RecursionError: maximum recursion depth exceeded while calling a Python object。

原因是self.__getattribute__会调用自身,所以就出现了死循环。通过supr(Person, self)来调用_XXX_(备注:这里指访问控制魔术方法)可以避免递归调用。

也有人通过self.__dict__的方式来访问或修改属性,这种方式看上去可行,但是存在一个问题,因为self.__dict__本身也是对象的属性(只是这个属性比较特殊,它存放了对象的其它属性),所以每次访问self.__dict__都会触发__getattribute__和__getattr__方法,这完全没必要,如果在这两个方法里面存在日志,会输出大量没必要的日志。所以建议通过supr(Person, self)来调用。