python:類與裝飾器
- 2019 年 11 月 21 日
- 筆記
簡單裝飾
在python中,裝飾器可以是一個類。就是這麼任性。 要把一個類做為裝飾器是很簡單的。這裡借鑒一個很有意思的程式碼,python管道語法糖的實現,作者JulienPalard 項目
class Pipe: def __init__(self, function): self.function = function functools.update_wrapper(self, function) def __ror__(self, other): return self.function(other) def __call__(self, *args, **kwargs): return Pipe(lambda x: self.function(x, *args, **kwargs))
可以這麼用:
@Pipe def average(iterable): """ Build the average for the given iterable, starting with 0.0 as seed Will try a division by 0 if the iterable is empty... """ total = 0.0 qte = 0 for x in iterable: total += x qte += 1 return total / qte [1, 3, 6] | average
神奇啊,神奇啊。我一看這份程式碼,就愛上了這個簡潔的小項目。真是太有BG了。 但是,這個項目有一個小問題。那就是,它不能裝飾類方法。
裝飾類方法
類的函數和普通函數非常相似,在調用的時候會以self關鍵字傳入當前實例作為參數。這是大家都明白的。 但值得一提的是,類函數有比較複雜的調用機制,當執行 cls.method的一瞬間,系統會調用該方法(注意不是類)的__get__(self, instance, cls)
函數。instance就是所屬類的實例,cls是類的簽名。 所以當用上面Pipe類直接裝飾一個類方法,將會丟失類的self關鍵字。要實現功能,必須獲取運行時的instance實例,要獲取instance,就必須hack__get__
函數。 所以,程式碼變化成了下面這樣:
import functools class Pipe: def __init__(self, function): self.function = function functools.update_wrapper(self, function) def __ror__(self, other): return self.function(other) def __get__(self, instance, cls): return Pipe(lambda x: self.function(instance, x))
要點如下: 1. 在get中是一個經過實例包裝的pipe類實例。這個實例包裝了instance。 2. 這個成員必須是Pipe類型,否則,普通的Lambda方法或者函數,沒有實現xor,自然也不能和|
連用。
這個類裝飾器,也兼容普通的函數,因為普通函數不會觸發__get__
方法。所以,行為與原項目一致。