1、類(lèi)和實(shí)例的訪問(wèn)
# 標(biāo)準(zhǔn)的描述符定義
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) # 描述器實(shí)例
def __init__(self, age):
self.age = age
@classmethod
def get_verson(cls):
return 1.0
@staticmethod
def find_version():
return 1.0
- 使用
vars() 或 __dict__查看對(duì)象的屬性
# Person 類(lèi)對(duì)象
>>> 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 實(shí)例對(duì)象
>>> per = Person(18)
>>> vars(per)
>>> {'age': 18}
# 可以看到,實(shí)例對(duì)象 per 只有 age 一個(gè)屬性,類(lèi) Person 有 name, get_version, find_version, weight 屬性
- 類(lèi)屬性可以使用實(shí)例或類(lèi)對(duì)象訪問(wèn),只有類(lèi)才能修改
>>> 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
# 屬性訪問(wèn)就是基于 Python 的描述器實(shí)現(xiàn)。類(lèi)或者實(shí)例通過(guò) . 操作符訪問(wèn)屬性,會(huì)先訪問(wèn)對(duì)象的 __dict__,如果沒(méi)有再訪問(wèn)類(lèi)(或父類(lèi),元類(lèi)除外)的 __dict__。如果最后這個(gè) __dict__ 的對(duì)象是一個(gè)描述器,則會(huì)調(diào)用描述器的 __get__ 方法
>>> p2.name # 等同于調(diào)用 p2.__dict__,發(fā)現(xiàn) name 不在此屬性字典中,則嘗試調(diào)用 type(p2).__dict__,發(fā)現(xiàn)此屬性存在,且此屬性的對(duì)象是字符串,故為 type(p2).__dict__['name']
>>> p2.weight # 和上述調(diào)用一致,不過(guò)在 type(p2).__dict__['weight']的對(duì)象是一個(gè)描述器,故最終調(diào)用如下 type(p2).__dict__['weight'].__get__(p2, type(p2))
>>> p1.get_version # 調(diào)用同上,發(fā)現(xiàn) p1 實(shí)例中沒(méi)有 get_version 屬性,且 type(p1).__dict__['get_version'] 的對(duì)象是 classmethod 實(shí)例,故最終調(diào)用如下 type(p1).__dict__['get_version'].__get__(Person)
# 同類(lèi)方法的調(diào)用
2、使用描述符對(duì)類(lèi)屬性做驗(yàn)證
# 驗(yàn)證正數(shù)的描述器
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
# 商品類(lèi)
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}
# 疑問(wèn)點(diǎn) a
>>> g.price = -1 # ValueError: 會(huì)執(zhí)行type(g).__dict__['price'].__set__(g, -1)
# 疑問(wèn)點(diǎn) b
>>> Good.price = -1 # TODO