很多使用Python的开发人员可能了解过 迭代器、生成器、装饰器。今天主要给大家分享Python的另外一种设计方法:描述器。什么是描述器?我们先来看下官方文档给出的摘要
1
2
| Defines descriptors, summarizes the protocol, and shows how descriptors are called. Examines a custom descriptor and several built-in Python descriptors including functions, properties, static methods, and class methods. Shows how each works by giving a pure Python equivalent and a sample application.
学习描述器不仅能提供接触到更多工具集的方法,还能更深地理解 Python 工作的原理并更加体会到其设计的优雅性。
| 描述器定义和简介一个描述器是一个包含“绑定行为”的对象,对其属性的存取被描述器协议中定义的方法覆盖。这些方法有:__get__()、__set__()、__delete__()。如果某个类中定义了这些方法中的任意一个,那么这个对象就可以被称为一个描述器。 实例对象属性访问的默认行为是从一个对象的__dict__字典类型的属性中获取、设置或删除。 下面的列子 对象p.name的查找顺序会从 p.__dict__[name]开始,然后是Person.__dict__[name],接下来依次查找Person的基类,不包括元类。 [Python] 纯文本查看 复制代码 class Person(object):
def __init__(self):
self.name = 'qiangzai'
p = Person()
print(p.__dict__) # {'name': 'qiangzai'}
p.age = 18 # 给p实例对象添加age属性
print(p.__dict__) # {'name': 'qiangzai', 'age': 18}
如果找到的值是一个定义了某个描述器方法的对象,则Python可能会重载默认行为并转而发起调用描述器方法。 这具体发生在优先级链的那个环节则要根据所定义的描述器方法及其调用的方式来决定。 非数据描述器实例对象中只定义了__get__()的描述器称为非数据描述器(它们通常用于方法,但也可以用于其他用途)。 案例1 定义一个非数据描述器我们定义了两个类 Descriptor和Person,其中Person的类属性name是Descriptor的实例对象。注意一定是类属性,如果是实例属性就会失效。 [Python] 纯文本查看 复制代码 class Descriptor(object):
def __get__(self, instance, owner):
# self 指A的实例对象 owner/instance指的是调用方的类及类的实例
print(self, instance, owner)
class Person(object):
# 注意一定是类属性,而不是实例属性
name = Descriptor()
p = Person()
p.name # <__main__.Descriptor object at 0x10c6c1940> <__main__.Person object at 0x10c782668> <class '__main__.Person'>
'''
p.name的调用顺序
1. p先去自己的__dict__去寻找name, 没有找到
2. p又去自己的类 Person的__dict__找name, 找到了
3. 找到以后返回name对应的value是一个描述器,会调用描述器的__get__方法
'''
Person.name # <__main__.Descriptor object at 0x10a3cd940> None <class '__main__.Person'>
'''
Person.name的调用顺序
1. Person去自己的属性Person.__dict__中找到了name
2. 发现找到的name对应的value是一个描述器,会调用描述器的__get__方法
''' [Python] 纯文本查看 复制代码 class Descriptor(object):
def __init__(self):
self.cls_name = 'Descriptor'
def __get__(self, instance, owner):
# 将实例对象self本身返回
return self
class Person(object):
name = Descriptor()
p = Person()
print(p.name.cls_name) # Descriptor 案例3 非数据描述器的问题[Python] 纯文本查看 复制代码 class Descriptor(object):
def __init__(self):
self.cls_name = 'Descriptor'
def __get__(self, instance, owner):
# 将实例对象self本身返回
return self
class Person(object):
name = Descriptor()
def __init__(self):
self.name = 'qiangzai'
p = Person()
print(p.name) # qiangzai
print(p.name.cls_name) # AttributeError: 'str' object has no attribute 'cls_name'
结论:当类属性和实例属性同名时.实例对象访问属性的优先级链 实例对象.__dict__ > 类.__dict__
|