一文搞懂__getattribute__,__getattr__,__setattr__,__delattr__

一文搞懂__getattribute____getattr____setattr____delattr__

__getattr__(self, name)

在属性被访问而对象没有这样的属性时自动调用,当属性可以通过正常机制追溯到时,__getattr__是不会被调用的

class Sudent:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        return '当{}属性不存在时调用我,存在就不用调用了'.format(item)


s = Sudent('zhangsan')
print(s.name)  # zhangsan
print(s.age)  # 当age属性不存在时调用我,存在就不用调用了

但是注意一定不能像下面这样调用

class Sudent:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        return self.item


s = Sudent('zhangsan')
print(s.name)  # zhangsan
print(s.age)

为什么呢?这样的话会出现无限递归错误

__getattribute__(self, name)

在属性被访问时自动调用(只适用于新式类),实例访问属性,__getattribute__方法一直会被调用,无论属性是否能够追溯到。如果类还定义了__getattr__方法,除非通过__getattribute__显式的调用它,或者__getattribute__方法出现AttributeError错误,否则__getattr__方法不会被调用了

class Sudent:
    def __init__(self, name):
        self.name = name

    def __getattribute__(self, item):
        return '不管{}属性存不存在都要调用我'.format(item)


s = Sudent('zhangsan')
print(s.name)  # 不管name属性存不存在都要调用我
print(s.age)  # 不管age属性存不存在都要调用我

同样的如果在__getattribute__方法下存在通过self.attr访问属性,会出现无限递归错误。

__setattr__(self, name, value)

试图给属性赋值时自动调用,有则改无则增

class Sudent:
    def __init__(self, name):
        self.name = name

    def __setattr__(self, key, value):
        print('属性{},属性值{}'.format(key, value))


s = Sudent('zhangsan')  # 属性name,属性值zhangsan
s.age = 18  # 属性age,属性值18
print(s.name)  # 报错
print(s.age)  # 报错

同样的__setattr__下还有self.attr的赋值操作就会出现无限递归的调用__setattr__的情况。并且自己实现__setattr__有很大风险,一般情况都还是继承object类的__setattr__方法

class Sudent:
    def __init__(self, name):
        self.name = name


s = Sudent('zhangsan')
s.__setattr__('name', 'lisi')
print(s.name)  # lisi
s.__setattr__('age', 18)
print(s.age)  # 18
__delattr__(self, name)

试图删除属性时自动调用。

class Sudent:
    def __init__(self, name):
        self.name = name

    def __delattr__(self, item):
        print('删除的属性是{}'.format(item))


s = Sudent('zhangsan')
del s.name  # 删除的属性是name
class Sudent:
    def __init__(self, name):
        self.name = name


s = Sudent('zhangsan')
print(s.name)  # zhangsan
s.__delattr__('name')
print(s.name)  # 报错

相比函数property,这些魔法方法使用起来要棘手些(从某种程度上说,效率也更低),但它们很有用,因为你可在这些方法中编写处理多个特性的代码

class Rectangle:
    def __init__ (self):
        self.width = 0
        self.height = 0
    def __setattr__(self, name, value):
        if name == 'size':
            self.width, self.height = value
        else:
            self. __dict__[name] = value
    def __getattr__(self, name):
        if name == 'size':
            return self.width, self.height
        else:
            raise AttributeError()
s = Rectangle()
print(s.size)  # (0, 0)
s.size = 10, 20
print(s.width)  # 10
  • 即便涉及的属性不是size,也将调用方法__setattr__。因此这个方法必须考虑如下两种情形:如果涉及的属性为size,就执行与以前一样的操作;否则就使用魔法属性__dict____dict__属性是一个字典,其中包含所有的实例属性。之所以使用它而不是执行常规属性赋值,是因为旨在避免再次调用__setattr__,进而导致无限循环。
  • 仅当没有找到指定的属性时,才会调用方法__getattr__。这意味着如果指定的名称不是size ,这个方法将引发AttributeError异常。这在要让类能够正确地支持hasattr和getattr等内置函数时很重要。如果指定的名称为size,就使用前一个实现中的表达式。
  • 编写方法__setattr__时需要避开无限循环陷阱,编写 __getattribute__时亦如此
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值