python中的属性访问顺序与描述符

对象属性的访问顺序

使用点操作符访问属性时,obj.value等价于getattr(obj, 'value'),其查找顺序为

  1. 实例属性,等价于使用obj.__dict__[value]
  2. 类属性,等价于使用type(obj).__dict__[value]
  3. 基类属性,遍历type(obj)的基类查找属性
  • obj的类里重写了__getattr__(self, item)方法,正常的属性访问找不到属性时会调用这个方法。

  • obj的类里重写了__getattribute__(self, item)方法,属性访问时无条件调用这个方法,一般不重写

class Animal(object): 
    num_animal = 0

class Cat(Animal):
    num_cat = 1
    def __init__(self):
        self.name = 'cat'

def __getattr__(self, item):
    print('function __getattr__ called')
    return None

cat = Cat()

print(Cat.__dict__) # {'__module__': '__main__', 'num_cat': 1, '__init__': <function Cat.__init__ at 0x0000022E613C4730>, '__getattr__': <function Cat.__getattr__ at 0x0000022E613C47B8>, '__doc__': None}
print(cat.__dict__) # {'name': 'cat'}
print(cat.name) # cat
print(cat.num_animal) # 0
print(cat.num_cat) # 1
print(cat.age) # None
# function __getattr__ called

描述符

python描述符:某种特殊的类的实例作为另一个类的属性。描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性

这种类需满足实现了以下至少一种特殊方法

  • __get__(self, instance, owner)
  • __set__(self, instance, value)
  • __delete__(self, instance)

描述符分类:

  • 描述符:类内定义了上面三个方法的任意一个,类的实例就会被认为是一个描述符

  • 数据描述符:类同时定义了__get__()__set__()方法

  • 非数据描述符:类只定义 __get__()方法

  • 只读数据描述符:类中同时定义__get__()__set__()方法,且在__set__()方法中抛出AttributeError异常

数据和非数据描述符的区别:如果一个实例的字典有和数据描述符同名的属性,那么数据描述符会被优先使用;实例的字典实现了非数据描述符的定义,那么这个字典中的属性会被优先使用

class NameDescriptor(object):
    def __init__(self):
        self.name = 'cat1'
    def __get__(self,instance,owner):
        print("function __get__ called")
        print('self:', self)
        print('instance:', instance)
        print('owner:', owner)
        return self.name
 
    def __set__(self,instance,value):
        print("function __set__ called")
        print('self:', self)
        print('instance:', instance)
        print('value:', value)
        self.name = value
 
class Cat(object):
    name= NameDescriptor() # 实例作为属性
 
cat=Cat()
print(cat.name) # cat1
# self: <__main__.NameDescriptor object at 0x0000026B738834A8>
# instance: <__main__.Cat object at 0x0000026B738834E0>
# owner: <class '__main__.Cat'>
cat.name='cat2' 
# function __set__ called
# self: <__main__.NameDescriptor object at 0x0000026B738834A8>
# instance: <__main__.Cat object at 0x0000026B738834E0>
# value: cat2
print(cat.name) # cat2

描述符使用@

class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length


    @Lazyproperty # area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length


r1=Room('alex',1,1)
print(r1.area)

参考

https://blog.csdn.net/weixin_38729390/article/details/86755682

https://www.cnblogs.com/ls-2018/p/8819740.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值