我們今天來看一段炫技代碼。它可以把任何能接收兩個參數的函數定義成一個特殊的運算符。
例如,我們知道Python裡面的range函數,最少可以接收1個參數,最多能夠接收3個參數。當隻有兩個參數的時候,格式為range(開始, 結束),從開始數字到結束數字逐次加1,左閉右開。使用今天的方法,可以把它的寫法改為開始 |到| 結束,如下圖所示:
又比如,urllib.parse.urljoin可以把域名和一個相對路徑拼接起來: urljoin(域名, 相對路徑),我們也可以改寫成如下圖所示的格式:
這種炫技有餘,實用不足的功能是怎麼實現的呢?其實原理非常簡單,隻有8行代碼:
class Test:
def __init__(self, num):
self.num = num
def __or__(self, other):
print(f'我右邊有一個東西,它是:{other}')
x = Test(100)
x | 551.2.3.4.5.6.7.8.
這裡就涉及到一個盲點和兩個真正的知識點。這個盲點就是,你可能以為 |到|是一個字符,但是它是3個字符;你可能會把|拼接|看做一個整體,但是它實際上是3個部分:左邊的|、拼接和右邊的|。
我們把空格加上,就很明顯了:
兩個真正的知識點,就是__or__和__ror__這兩個魔術方法和偏函數partial。而Change本身就是一個普通的類而已,__or__和__ror__定義了這個類的實例在左側遇到|時,右側遇到|時的具體行為。
我們一個一個來講。首先是__or__。它定義了實例的右側遇到|時的具體行為。例如,我們用一個簡單的代碼來進行測試:
class Test:
def __init__(self, num):
self.num = num
def __or__(self, other):
print(f'我右邊有一個東西,它是:{other}')
x = Test(100)
x | 551.2.3.4.5.6.7.8.
運行效果如下圖所示:
但如果你把豎線放在左邊,他就會報錯,如下圖所示:
而__ror__就是用來定義|在實例左邊的時候,它的行為:
所以,我們最開始的例子中,2 |到| 10,實際上應該理解為:
在我們演示的例子中,2 | 到首先進入了Change類的__ror__方法中:
複制 def __ror__(self, other):
self.func = partial(self.func, other)
return self1.2.3.
其中,一開始的self.func就是我們在初始化實例Change(range)時傳入的參數range。所以partial(self.func, other)等價于partial(range, 2)。關于偏函數partial,大家可以看我這篇文章:偏函數:在Python中設定默認參數的另一種辦法。簡單來說,使用偏函數,可以給一個真正的函數傳一部分參數,過一會再補剩下的參數。
可能大家在日常的開發者,很少會讓一個實例方法返回self。關于這個寫法,大家可以看我的這一篇文章:一日一技:在Python裡面實現鍊式調用。也就是說,1 | 到返回的,依然是Change類的一個實例,我們簡稱它為x。這個實例的屬性self.func的值是partial(range, 2)。
接下來,x | 10,調用的是__or__方法,于是,此時執行的是partial(range, 2)(10)。偏函數的參數補全了,于是它裡面的range真正運行了起來,成為了range(2, 10)。
至此,這個Change類我們就解析透了。大家知道,在Python裡面,魔術方法是有很多的,如果你不想用|,你還可以用其它的,例如:
或者:
或者:
同時,這個Change類,你甚至可以直接當做裝飾器來使用。任何能夠接收兩個參數的函數,都能使用這個裝飾器。例如:
最後總結一下。大家都知道,我是非常反對在工作代碼中炫技的,因為炫技的寫法很難讀,很難維護。今天這個炫技的方法,雖然我也不推薦大家用在工作中,但是它短短8行代碼裡面,包含了很多個知識點,這就值得大家玩一玩了。
來源: 未聞Code
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!