Python擴展方法一二事


前言

跟着一個有強迫症的老板干活是一件極其幸福的事情(你懂的)。最近碰到一個問題,簡單的說就是對一個對象做出部分修改后仍然返回此對象,於是我就寫了一個方法,老板看了之后只有一句話:不雅觀,改成直接對此對象調用此方法。我腦海里千萬個不情願,然而沒有辦法,不得不低頭,精通C#、Java、Scala等多種語言HelloWorld的我,一想便知這是擴展方法。於是開始Google之,看似簡單的問題,其實里面也有一些細節需要注意,在此記錄之。

Level 1

原理很簡單,將方法的第一個對象改成self(self即為需要擴展方法的對象的實例),其余不變,無需返回此對象,在此方法外部將此方法賦給此類。當需要調用的時候引入此包即可。大致如下:

class A():
    def m2():
        ...
        
    ...
    
def m1(self):
    // TODO: Do sth to this instance.
    ...
    
A.m1 = m1

a = A()
a.m1()

當然m1方法也可以添加其他參數,只需要放在self后面即可。

Level 2

過了幾天,老板又提了一個新的需求:某個類有一個m1方法,但是老板想修改此方法,使其更加完善。看上去沒有什么難的嗎,同樣是個擴展方法,於是我寫代碼如下:

class A():
    def m1():
        ...
       
    def m2():
        ...
    ...
    
def m1(self):
    // TODO: Do sth to this instance.
    self.m1()
    ...
    
A.m1 = m1

a = A()
a.m1()

滿心歡喜本以為這么簡單的就解決了問題。調試,悲劇的是直接報錯,錯誤為遞歸調用。細細想來確實如此,在此作用域內直接對self對象調用m1方法,應當是已經擴展后的方法,那么當然會產生循環調用的問題。

我想到應當可以通過先修改類中m1方法名稱來解決此問題,但是具體不知道如何操作,於是在StackOverflow中提了個問題,很快就有老外大牛回復了。見https://stackoverflow.com/questions/46460250/how-to-call-a-method-that-has-been-overridden-by-an-extension-method#。所以問題的核心在於我們可以通過下述代碼定義一個類的方法:

_m1 = A.m1

同樣調用的時候將實例作為第一個參數傳入。於是代碼進行如下改造即可:

class A():
    def m1():
        ...
       
    def m2():
        ...
    ...

_m1 = A.m1

def m1(self):
    // TODO: Do sth to this instance.
    _m1(self)
    ...
    
A.m1 = m1

a = A()
a.m1()

這樣即解決了遞歸調用的問題,但是此處有一個細節需要注意,_m1必須定義在m1方法上部,由於_m1是定義在m1擴展方法之上的,所以此處仍是A類中的m1方法,若是定義在m1擴展方法中,則又會出現遞歸調用的問題。

Level 3

下午看了一下上面在StackOverflow中提的問題,又有一個新的答案,分析了一下他主要介紹了兩點知識,覆蓋類的方法和覆蓋實例的方法,即擴展方法對整個類生效和只對某個實例生效。

對整個類擴展

除了直接寫A.plot = plot外,還可以寫成:

setattr(A, 'plot', plot)

A代表需要擴展的類,'plot'為擴展后的方法名,plot為重寫的擴展方法。當然如果擴展后的方法名在原類中已有,則覆蓋之;若無則為新的方法。

對具體實例擴展

讓我比較意外的是python可以對某個實例進行方法擴展,這在其他語言中似乎是基本沒有的。

當然我們不能用a.plot = plot的形式為a這個A的實例擴展方法,但是可以通過下述方式對a擴展方法:

a.plot = types.MethodType(plot, a)

這樣只有a有此擴展方法,或者是只有a的此方法被修改了,其他實例並不受影響。

總結

本文簡單記錄了Python擴展方法實現方式、類的方法的重定義、實例方法擴展等細節,供需查看。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM