摘要
七天假期最后一天边锁,假期賬戶余額已嚴(yán)重不足,那就總結(jié)下這兩天的學(xué)習(xí)內(nèi)容吧波岛。本文主要講解Python中另一種高級(jí)特性描述器茅坛,學(xué)習(xí)如何自定義描述器,和幾個(gè)內(nèi)置的描述器则拷,包括函數(shù)贡蓖、property、靜態(tài)方法和類方法煌茬。并運(yùn)用我們所學(xué)的知識(shí)斥铺,用純Python實(shí)現(xiàn)上述方法。最后介紹描述器的幾個(gè)運(yùn)用場(chǎng)景坛善,并給出真實(shí)的例子說(shuō)明晾蜘。
定義
描述器單獨(dú)出現(xiàn)是無(wú)意義的,它總是和其他類的類屬性一起出現(xiàn)浑吟,事實(shí)上笙纤,描述器描述了類屬性的訪問(wèn)、賦值和刪除行為组力,所以它被叫做描述器
描述器是怎么工作的
描述器僅僅是一個(gè)對(duì)象省容,跟其他對(duì)象相比并沒(méi)有內(nèi)在的特殊之處。就像Python其他強(qiáng)大特性一樣燎字,它們非常的簡(jiǎn)單腥椒。想要深入了解描述器協(xié)議阿宅,有三個(gè)條件需要搞清楚:
- 你有一個(gè)新式的類
- 它有類屬性
- 這個(gè)類屬性有描述器的幾個(gè)方法
上面所說(shuō)的描述器協(xié)議,包括以下幾個(gè)方法: -
__get__(self, instance, ower) --> value
獲得對(duì)象的屬性obj.description
相當(dāng)于description.__get__(obj笼蛛, OwerClass)
獲得類的屬性OwnerClass.descriptor
相當(dāng)于descriptor.__get__(None, OwnerClass)
-
__set__(self, instance, value) --> None
給對(duì)象賦值obj.descriptor = 5
相當(dāng)于descriptor.__set__(obj, 5)
-
__delete__(self, instance)
刪除對(duì)象屬性
del obj.description
相當(dāng)于description.__delete__(obj)
這邊有個(gè)易混淆的地方:描述器屬性被觸發(fā)是因?yàn)閷?duì)象的屬性(只有描述器對(duì)象是其他類的類屬性時(shí)候才起作用)洒放,但是在上述三個(gè)方法中self
是描述器本身,而不是對(duì)象
簡(jiǎn)單的描述器
class Int:
def __init__(self, name):
print('Int init')
self.name = name
def __get__(self, instance, cls):
print('access')
print('{!r} {!r} {!r}'.format(self, instance, cls))
if instance is None:
return self
print(instance.__dict__)
return instance.__dict__[self.name]
def __set__(self, instance, value):
print('set')
print('{!r} {!r} {!r}'.format(self, instance, value))
instance.__dict__[self.name] = value
print(instance.__dict__)
def __delete__(self, instance):
pass
class A:
x = Int('x')
print(x)
def __init__(self, x):
print('A init')
self.x = x
# out: 在定義類A的時(shí)候就會(huì)生出類屬性x
Int init
<__main__.Int object at 0x7fcb38f916a0>
a = A(4)
# out:
A init
set
<__main__.Int object at 0x7fcb38f916a0> <__main__.A object at 0x7fcb38ff5a20> 4
{'x': 4}
a.x
# out:
access
<__main__.Int object at 0x7fcb3874aeb8> <__main__.A object at 0x7fcb386b12e8> <class '__main__.A'>
{'x': 4}
4
描述器協(xié)議非常的簡(jiǎn)單滨砍,用途十分廣泛往湿,以至于他們被打包成獨(dú)立的函數(shù)。像屬性(property)惋戏、靜態(tài)方法和類方法都是基于描述器協(xié)議的领追。
屬性(property)
下面是一個(gè)純Python實(shí)現(xiàn)的property
:
class Property:
def __init__(self, fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, cls):
print('get')
print(instance)
print(self.fget)
print(cls)
if instance is None:
return self
if not callable(self.fget):
raise AttributeError()
print(self.fget(instance))
return self.fget(instance)
def __set__(self, instance, value):
print('set')
if not callable(self.fset):
raise AttributeError()
print(self.fset)
self.fset(instance, value)
def __delete__(self, instance):
print('delete')
if not callable(self.fdel):
raise AttributeError()
self.fdel(instance)
def setter(self, fset):
print('setter')
print(fset)
return Property(self.fget, fset, self.fdel)
def deleter(self, fdel):
return Property(self.fget, self.fset, fdel)
class A:
def __init__(self, x):
self.__x = x
@Property
def x(self): # x = Property(A.x)
return self.__x
@x.setter # x = x.setter(A.x) = Property(x.fget, A.x, x.fdel)
def x(self, value):
self.__x = value
# out:
setter
<function A.x at 0x7fcb38f9fc80>
a = A(10)
a.x
# out:
get
<__main__.A object at 0x7fcb387439b0>
<function A.x at 0x7fcb3873bd08>
<class '__main__.A'>
10
a.x = 11
# out:
set
<function A.x at 0x7fcb3873bc80>
上述代碼中我加了很多打印,能夠幫助大家理解响逢,如果覺(jué)得繁瑣绒窑,可以刪除上述打印。
-
Property
對(duì)象是描述器 -
Property.setter
和Property.deleter
都是裝飾器舔亭,他們返回的是Property()
對(duì)象些膨,不同的是@Property
設(shè)置的是fget
,setter
和deleter
分別設(shè)置fset
和fdel
類方法
純Python實(shí)現(xiàn)類方法钦铺,代碼如下:
from functools import wraps, partial
class Classmethod:
def __init__(self, method):
wraps(method)(self)
def __get__(self, instance, cls):
return partial(self.__wrapped__, cls)
class C:
@Classmethod
def method(cls):
print(cls)
def method2(self, x):
print(cls)
print(x)
C.method()
# out:
class method
<class '__main__.C'>
上述代碼完美的實(shí)現(xiàn)了類方法裝飾器订雾,有一個(gè)函數(shù)wraps()
需要著重講解一下
wraps(fn)(g)
把fn
的一些屬性(比如__module__, __name__, __qualname__, __doc__,__annotations__
),復(fù)制給g
(主要是更新g
的__dict__
)矛洞,g
就有一個(gè)屬性叫做__wrapped__
葬燎,g.__wrapped__
就等價(jià)于fn
描述器的一個(gè)應(yīng)用
用描述器做類型檢查
from inspect import signature
class Typed:
def __init__(self, name, required_type):
self.name = name
self.required_type = required_type
def __get__(self, instance, cls):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.required_type):
raise TypeError('{} required {}'.format(self.name, self.required_type))
instance.__dict__[self.name] = value
def typeassert(cls):
sig = signature(cls)
for k, v in sig.parameters.items():
setattr(cls, k, Typed(k, v.annotation))
return cls
@typeassert
class Person:
def __init__(self, name: str, age: int): # Python3.5最新類型提示
self.name = name
self.age = age
p = Person(18, 'xus')
# out:
TypeError: name required <class 'str'>
結(jié)語(yǔ)
希望大家對(duì)Python描述器有了一個(gè)全新的認(rèn)識(shí),加油吧騷年缚甩!