天天看點

動态修改Python類和執行個體的方法(轉)

相信很多朋友在程式設計的時候都會想修改一下已經寫好的程式行為代碼,而最常見的方式就是通過子類來重寫父類的一些不滿足需求的方法。比如說下面這個例子。

class Dog:
    def bark(self):
        print 'Woof!'
 
class Husky(Dog):
    def bark(self)
        print 'Howl!'      

我們可以用上述方式來修改我們自己寫的代碼,但是我們應該怎麼修改第三方代碼呢?當然,我們也可以自己編寫一個子類,調用子類的執行個體對象來實作修改,但是這樣可能會引入其他一系列問題。是以我們得想個辦法用我們自己的方法替換掉原來的對象方法,這就是本文接下來要介紹的“打更新檔”的方式。

給類打更新檔

如果我們想新增或是修改對象的方法的話,最簡單的方式莫過于給類打個更新檔了。結合上面的例子,如果我們想給我們自己的 

Dog

 類寫一個新的 

howl

 方法的話,我們可以定義一個新的 

howl

 函數,像下面的代碼一樣把它添加到我們的類中:

def newbark(self):
    print 'Wrooof!'
 
def howl(self):
    print 'Howl!'
 
# Replace an existing method
Dog.bark = newbark
 
# Add a new method
Dog.howl = howl      

很簡單吧?但是這裡有幾個問題需要我們注意。首先,被修改的類的所有執行個體中的方法都會被更新,是以更新後的方法不僅僅存在于新建立的對象中,之前建立的所有對象都會擁有更新之後的方法,除非隻是新增而不是覆寫掉原來的方法。第二,你修改或者新增的方法應當是與對象綁定的,是以方法的第一個參數應當是被調用的對象(在這裡就是類的執行個體

self

)。

給類執行個體打更新檔

單個對象也可以在不影響這個類的其他執行個體的情況下打更新檔。但是還是有點小技巧的哦!先讓我們看看下面這個例子。

def herd(self, sheep):
    self.run()
    self.bark()
    self.run()
 
border_collie = Dog()
border_collie.herd = herd      

然後我們再試試調用新定義的方法:

border_collie.herd(sheep)
 
TypeError: herd() takes exactly 2 arguments (1 given)
The problem with the previous code is that the herd is not a bound method, just take a look at the following code:
 
print border_collie.herd
 
<function herd at 0xf9c5f0>      

出錯啦!引發錯誤的原因就是被調用的對象并沒有作為第一個參數傳給我們寫的函數。當然我們可以自己把參數傳進去,但是在這個替換類方法的場景下并不奏效。解決這個問題的正确方案是用 

type

 這個子產品裡的 

MethodType

 函數,我們可以看看下面的示例代碼:

import types
 
border_collie = Dog()
border_collie.herd  = types.MethodType(herd, border_collie)
 
print border_collie.herd
<bound method ?.herd of <__main__.Dog instance at 0x23c9518>>
 
border_collie.herd(sheep)      

現在我們的方法已經和執行個體綁定了,大功告成!

總結

運作中替換或者添加方法是非常有用的,比如說在單元測試中,有些負責和外界服務通信的函數就需要替換掉,友善測試。這個技巧不僅很常用,而且在你最終決定要修改代碼之前還可以保持代碼的可維護性,是一個非常重要的技巧。