python類裝飾器以及描述器作為裝飾器
阿新 • • 發佈:2019-02-11
1. 類裝飾器
常見的裝飾器一般以函式(方法)的方式實現,根據裝飾器的原理,原函式或方法被裝飾後能得到一個新的可呼叫物件,那麼其實類也能實現這個功能,只要這個類(例項)是可呼叫的,即實現了__call__方法。
# demo
class Decorator:
def __init__(self, func):
self._func = func
def __call__(self, name, **kwargs):
# 判斷簡名字是否存在
print('______Decorator call_______')
if name in ('Ben', 'Mike'):
return self._func(name, **kwargs)
return
# 等同與get_fullname = Decorator(get_fullname)
@Decorator
def get_fullname(name):
# 根據簡名獲取全名
print('---------test-----------')
return name + ' kely'
if __name__ == '__main__':
print(get_fullname('Lily' ))
"""
輸出:
______Decorator call_______
None
"""
print(get_fullname('Mike'))
"""
輸出:
______Decorator call_______
---------test-----------
Mike kely
"""
2. 描述器作為類方法裝飾器
看到這個標題可能會有點懵逼,如果不懂什麼是描述器可以看我之前寫的文章:屬性訪問一文中有說明。實際上實現了__get__方法的類就是描述器。
這裡就是要用描述器來裝飾類方法,因為描述器的行為決定了它只能在類中。並且被描述器裝飾的方法的行為與常規的方法的行為不同。
- 行為1: 把方法變成屬性進行訪問
class Descriptor:
def __init__(self, func):
print('______Descriptor init_______')
self._func = func
def __get__(self, instance, owner):
print('______Descriptor get_______')
name = self._func(instance)
return name
# return self._func
class Test:
# 等同與name = Descriptor(name)
@Descriptor
def name(self, name=None):
print('______Test name_______')
if name is None:
return 'hello'
return name
if __name__ == '__main__':
t = Test()
print(t.name)
"""
______Descriptor init_______
______Descriptor get_______
______Test name_______
hello
"""
十分關鍵:這樣使用的時候,一定要把原本的函式儲存下來self._func = func,在__get__通過self._func(instance)進行訪問,並且name從外界來看已經是Test的一個屬性,而不是一個方法,這樣的實現方式很類似@property裝飾器的作用,但功能更強大,更加靈活。
- 行為2:如果要給這個name方法傳遞引數怎麼辦
class Descriptor:
...
def __get__(self, instance, owner):
print('______Descriptor get_______')
return self._func
if __name__ == '__main__':
t = Test()
print(t.name(t, 'Ben'))
這樣就實現了引數的傳遞,好奇的人會注意到引數中還包括了例項t,這是因為t.name是Descriptor的self._func屬性,而self._func是一開始通過func(看一開始的程式碼)賦予的值,傳遞過來的func值並不會記錄下例項的物件,所以需要傳遞例項物件給func.
也許你會發現,行為2的情況實際上有第一種類裝飾器就可以很好的解決,用第二種很顯得更加變扭,所以只推薦第一種行為的時候使用描述器作為裝飾器