什么是描述符
官方的定义:描述符是一种具有“捆绑行为”的对象属性。访问(获取、设置和删除)它的属性时,实际是调用特殊的方法(_get_()
,_set_()
,_delete_()
)。也就是说,如果一个对象定义了这三种方法的任何一种,它就是一个描述符。
描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在被使用类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例,我们可以通过查看实例和类的字典来确认这一点。
描述符是实现大部分Python类特性中最底层的数据结构的实现手段,我们常使用的@classmethod
、@staticmethd
、@property
、甚至是__slots__
等属性都是通过描述符来实现的。它是很多高级库和框架的重要工具之一,是使用到装饰器或者元类的大型框架中的一个非常重要组件。
普通对象
示例
class Parent:
name = "parent"
class Son(Parent):
name = "son"
s = Son()
s.name = "other"
print(s.name)
------------------------------------------------------------------------
other
普通的python对象操作(增删改查)它的属性时,都是基于该对象的__dict__
基础之上进行的。比如,上例中访问实例对象s的属性name: s.name
的时候,查找顺序如下所示:
- 通过实例对象的
__dict__
属性访问:s.__dict__['name']
- 通过类型对象的
__dict__
属性访问:type(s).__dict__['name']
等价于Son.__dict__['name']
- 通过父类对象的
__dict__
属性访问:s.__class__.__base__.__dict__['name']
等价于Parent.__dict__['name']
类似地修改属性name的值也是通过__dict__的方式:
s.__dict__['name'] = 'zhangsan'
print s.name
------------------------------------------------------------------------
zhangsan
描述符
class Descriptor:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
print("__get__")
print("instance:", instance)
print("owner:", owner)
return self.name
def __set__(self, instance, value):
print("__set__", instance, value)
self.name = value
class Man:
name = Descriptor("张三")
someone = Man()
print(someone.name)
---------------------------------------------------------------------
__get__
instance: <__main__.Man object at 0x0000026B570D9E48>
owner: <class '__main__.Man'>
张三
这里的Descriptor就是一个描述符,访问Man对象的name属性时不再是通过__dict__
属性来访问,而是通过调用Descriptor的__get__
方法获取,同样的道理,给name赋值的时候是通过调用__set__
方法实现而不是通过__dict__
属性。
通过__dict__
赋值,点号取值
someone.__dict__['name'] = '李四'
print(someone.name)
-----------------------------------------------------------------------
__get__
instance: <__main__.Man object at 0x0000026B570D9048>
owner: <class '__main__.Man'>
张三
通过点号赋值
someone.name = "王二"
print(someone.name)
------------------------------------------------------------------------
__set__ <__main__.Man object at 0x0000026B570D9D88> 王二
__get__
instance: <__main__.Man object at 0x0000026B570D9D88>
owner: <class '__main__.Man'>
王二
类似地,删除属性的值也是通过调用__delete__
方法完成的。
数据描述符和非数据描述符
一个类,如果只定义了__get__
方法,而没有定义__set__
, __delete__
方法,则认为是非数据描述符;反之,则成为数据描述符。
实例属性和类属性优先级高于非数据描述符,但低于数据描述符。
class Descriptor:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
print("__get__")
print("instance:", instance)
print("owner:", owner)
return self.name
class Man:
name = Descriptor("张三")
m = Man()
# 实例属性
print("1:", m.name)
m.name = "李四"
print("2:", m.name)
print("3", Man.name)
Man.name = "王二"
print("4", Man.name)
-----------------------------------------------------------------------
__get__
instance: <__main__.Man object at 0x000001DDC4FDE1C8>
owner: <class '__main__.Man'>
1: 张三
2: 李四
__get__
instance: None
owner: <class '__main__.Man'>
3 张三
4 王二
属性查询优先级
__getattribute__
- 数据描述符:由1触发调用,若人为的重载了该
__getattribute__
方法,可能会调职无法调用描述符 - 实例对象的字典
- 类的字典
- 非数据描述符
- 父类的字典
-
__getattr__
方法