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 的描述器實現(xiàn)潘悼。類或者實例通過 . 操作符訪問屬性,會先訪問對象的 __dict__爬橡,如果沒有再訪問類(或父類治唤,元類除外)的 __dict__。如果最后這個 __dict__ 的對象是一個描述器糙申,則會調(diào)用描述器的 __get__ 方法
>>> p2.name # 等同于調(diào)用 p2.__dict__宾添,發(fā)現(xiàn) name 不在此屬性字典中,則嘗試調(diào)用 type(p2).__dict__柜裸,發(fā)現(xiàn)此屬性存在缕陕,且此屬性的對象是字符串,故為 type(p2).__dict__['name']
>>> p2.weight # 和上述調(diào)用一致疙挺,不過在 type(p2).__dict__['weight']的對象是一個描述器扛邑,故最終調(diào)用如下 type(p2).__dict__['weight'].__get__(p2, type(p2))
>>> p1.get_version # 調(diào)用同上,發(fā)現(xiàn) p1 實例中沒有 get_version 屬性铐然,且 type(p1).__dict__['get_version'] 的對象是 classmethod 實例蔬崩,故最終調(diào)用如下 type(p1).__dict__['get_version'].__get__(Person)
# 同類方法的調(diào)用
2恶座、使用描述符對類屬性做驗證
# 驗證正數(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
# 商品類
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: 會執(zhí)行type(g).__dict__['price'].__set__(g, -1)
# 疑問點 b
>>> Good.price = -1 # TODO