1. 程式人生 > >python類裝飾器以及描述器作為裝飾器

python類裝飾器以及描述器作為裝飾器

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的情況實際上有第一種類裝飾器就可以很好的解決,用第二種很顯得更加變扭,所以只推薦第一種行為的時候使用描述器作為裝飾器