[Python] pipe模組

小贼的自由發表於2024-05-23

一. 概述

我們都知道在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()的作用過程是這樣的:

  1. Pipe裝飾器包裝函式Filter
  2. Filter()的呼叫實際上就是呼叫Pipe例項中的__call__方法;
  3. __call__方法中,用lambda對實際的Filter函式再做一次封裝(其實這個lambda可以省去的,可自行驗證),並傳入該lambda表示式生成一個新的Pipe物件;
  4. 之後是將GetNums()函式的返回值作為運算元,傳入到__ror__中;
  5. __ror__方法中,呼叫self.function(實際的Filter函式)處理處理資料,返回結果。

相關文章