1、类和实例的访问
# 标准的描述符定义
class Quanty:
def __init__(self, weight):
self.weight = weight
def __get__(self, instance, cls):
return self.weight
def __set__(self, instance, value):
self.weight = value
class Person:
name = 'PY_ONE'
weight = Quanty(90) # 描述器实例
def __init__(self, age):
self.age = age
@classmethod
def get_verson(cls):
return 1.0
@staticmethod
def find_version():
return 1.0
- 使用
vars()
或 __dict__
查看对象的属性
# Person 类对象
>>> vars(Person)
>>> mappingproxy({
'__dict__': <attribute '__dict__' of 'Person' objects>,
'__doc__': None,
'__init__': <function __main__.Person.__init__>,
'__module__': '__main__',
'__weakref__': <attribute '__weakref__' of 'Person' objects>,
'find_version': <staticmethod at 0x10544ed30>,
'get_verson': <classmethod at 0x10544e240>,
'name': 'PY_ONE',
'weight': <__main__.Quanty at 0x105472cc0>
})
# Person 实例对象
>>> per = Person(18)
>>> vars(per)
>>> {'age': 18}
# 可以看到,实例对象 per 只有 age 一个属性,类 Person 有 name, get_version, find_version, weight 属性
>>> p1, p2 = Person(16), Person(17)
>>> p1.name # PY_ONE
>>> p2.name # PY_ONE
>>> Person.name # PY_ONE
>>> p1.name = 'PY_WJ'
>>> Person.name # PY_ONE
>>> p2.name # PY_ONE
>>> p1.name # PY_ONE
>>> Person.name = '_PY'
>>> p2.name # _PY
>>> p1.name # PY_WJ
# 属性访问就是基于 Python 的描述器实现。类或者实例通过 . 操作符访问属性,会先访问对象的 __dict__,如果没有再访问类(或父类,元类除外)的 __dict__。如果最后这个 __dict__ 的对象是一个描述器,则会调用描述器的 __get__ 方法
>>> p2.name # 等同于调用 p2.__dict__,发现 name 不在此属性字典中,则尝试调用 type(p2).__dict__,发现此属性存在,且此属性的对象是字符串,故为 type(p2).__dict__['name']
>>> p2.weight # 和上述调用一致,不过在 type(p2).__dict__['weight']的对象是一个描述器,故最终调用如下 type(p2).__dict__['weight'].__get__(p2, type(p2))
>>> p1.get_version # 调用同上,发现 p1 实例中没有 get_version 属性,且 type(p1).__dict__['get_version'] 的对象是 classmethod 实例,故最终调用如下 type(p1).__dict__['get_version'].__get__(Person)
# 同类方法的调用
2、使用描述符对类属性做验证
# 验证正数的描述器
class PositiveNum:
def __init__(self, key_col):
self._key_col = key_col
def __get__(self, instance, cls):
return instance.__dict__[self._key_col]
def __set__(self, instance, value):
if value < 0:
raise ValueError('value must be > 0')
instance.__dict__[self._key_col] = value
# 商品类
class Good:
price = PositiveNum('price')
quantity = PositiveNum('quantity')
def __init__(self, price, quantity):
self.price = price
self.quantity = quantity
>>> g = Good(12, -1) # ValueError
>>> g = Good(12, 8) # {'price': 12, 'quantity': 8}
# 疑问点 a
>>> g.price = -1 # ValueError: 会执行type(g).__dict__['price'].__set__(g, -1)
# 疑问点 b
>>> Good.price = -1 # TODO