一. 概述
我們都知道在Linux下執行命令ls | sort -r
會將排序後的結果進行輸出,它是先獲取目錄資料,管道符|
將ls的輸出作為後一個命令的輸入,最終得到反向排序的結果。
Python和其他大多數語言一樣,處理資料的時候通常是將資料作為引數傳入。但是有沒有像Linux管道符那樣優雅的輸出呢?有的,這就是pipe
模組,但需要注意的是pipe
模組並不屬於Python的標準庫,可用pip pipe
進行安裝。
二. 使用
pipe
的使用方法很簡單,只需要給處理資料的函式加上@Pipe
註解就OK了。如下,
from pipe import *
@Pipe
def Filter(lst):
return [x for x in lst if x > 3]
def GetNums():
return [1, 2, 3, 4, 5]
res = GetNums() | Filter()
print(res)
輸出:
[4, 5]
三. 原理
pipe
的實現原理和它的使用方式一樣簡單(正因為簡單,才有這篇筆記(╥﹏╥)
)。它的核心程式碼只有幾行,實際就是一個裝飾器。
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))
好,既然這麼簡單。我們第二章中的例子自己實現一次,並新增了一些註釋和輔助分析的輸出。
# 裝飾器
class Pipe:
def __init__(self, function):
print(f"__init__呼叫,例項id為:{id(self)}")
self.function = function
# # 保留傳入函式的元資訊,可忽略
# functools.update_wrapper(self, function)
def __ror__(self, other):
print(f"__ror__被呼叫,傳入的引數是:{other}")
return self.function(other)
def __call__(self, *args, **kwargs):
print("__call__被呼叫")
# 返回的是一個Pipe物件,這樣就能夠使用過載的“|”
# 傳入一個lambda表示式,是對self.function的再一次封裝,回報存在這個Pipe物件的self.function中
return Pipe(lambda x: self.function(x, *args, **kwargs))
@Pipe
def Filter(lst):
print("Filter被呼叫")
return [x for x in lst if x > 3]
print("----- 分割線 -----")
def GetNums():
return [1, 2, 3, 4, 5]
res = GetNums() | Filter()
print(res)
輸出:
__init__呼叫,例項id為:2398974542568
----- 分割線 -----
__call__被呼叫
__init__呼叫,例項id為:2398974542624
__ror__被呼叫,傳入的引數是:[1, 2, 3, 4, 5]
Filter被呼叫
[4, 5]
從結果可以看到,Filter()
的作用過程是這樣的:
Pipe
裝飾器包裝函式Filter
;Filter()
的呼叫實際上就是呼叫Pipe
例項中的__call__
方法;- 在
__call__
方法中,用lambda對實際的Filter
函式再做一次封裝(其實這個lambda可以省去的,可自行驗證),並傳入該lambda表示式生成一個新的Pipe
物件; - 之後是將
GetNums()
函式的返回值作為運算元,傳入到__ror__
中; - 在
__ror__
方法中,呼叫self.function
(實際的Filter
函式)處理處理資料,返回結果。