2008-12-27 143 views
55

有沒有在Python中重寫實例級別的類方法的方法? 例如:覆蓋實例級別的方法

class Dog: 
    def bark(self): 
     print "WOOF" 

boby = Dog() 
boby.bark() # WOOF 
# METHOD OVERRIDE 
boby.bark() # WoOoOoF!! 

回答

22

請如圖所示不要這樣做。當您將一個實例與類相區別時,您的代碼變得不可讀。

您無法調試monkeypatched代碼。

當您在bobyprint type(boby)中發現錯誤時,您會看到(a)它是一隻狗,但(b)出於某種不明確的原因,它不會正確地叫出來。這是一場噩夢。不要做。

請替換。

class Dog: 
    def bark(self): 
     print "WOOF" 

class BobyDog(Dog): 
    def bark(self): 
     print "WoOoOoF!!" 

otherDog= Dog() 
otherDog.bark() # WOOF 

boby = BobyDog() 
boby.bark() # WoOoOoF!! 
120

是的,這是可能的:

class Dog: 
    def bark(self): 
     print "Woof" 

def new_bark(self): 
    print "Woof Woof" 

foo = Dog() 

funcType = type(Dog.bark) 

# "Woof" 
foo.bark() 

# replace bark with new_bark for this object only 
foo.bark = funcType(new_bark, foo, Dog) 

foo.bark() 
# "Woof Woof" 
+1

一個小小的評論,當你做funcType(new_bark,foo,Dog)它的名字==樹皮和實例方法Dog.new_bark在foo .__ dict__中加入嗎?因此,當您再次調用它時,首先查找實例字典並請求它, – 2013-11-29 02:23:18

+5

請解釋它的作用,特別是`funcType`的作用以及爲什麼它是必需的。 – 2016-07-13 04:30:17

+1

我認爲這可以通過使用`funcType = types.MethodType`(在導入`types`後)而不是`funcType = type(Dog.bark)`來使得更簡單和更明確。 – 2016-11-05 23:28:45

22
class Dog: 
    def bark(self): 
     print "WOOF" 

boby = Dog() 
boby.bark() # WOOF 

# METHOD OVERRIDE 
def new_bark(): 
    print "WoOoOoF!!" 
boby.bark = new_bark 

boby.bark() # WoOoOoF!! 

可以使用boby變量的函數裏面,如果你需要的。既然你重寫了這個實例對象的方法,這種方法更簡單,並且與使用self的效果完全相同。

0

因爲函數是在Python第一類對象可以通過他們在初始化您的類對象或隨時​​覆蓋它對於給定的類的實例:

class Dog: 
    def __init__(self, barkmethod=None): 
     self.bark=self.barkp 
     if barkmethod: 
      self.bark=barkmethod 
    def barkp(self): 
     print "woof" 

d=Dog() 
print "calling original bark" 
d.bark() 

def barknew(): 
    print "wooOOOoof" 

d1=Dog(barknew) 
print "calling the new bark" 
d1.bark() 

def barknew1(): 
    print "nowoof" 

d1.bark=barknew1 
print "calling another new" 
d1.bark() 

,結果

calling original bark 
woof 
calling the new bark 
wooOOOoof 
calling another new 
nowoof 
-4

雖然我很喜歡從·洛的傳承理念,與「類型(一)」的東西達成一致,但由於功能也有訪問屬性,我認爲它可以管理這種方式:

class Dog: 
    def __init__(self, barkmethod=None): 
     self.bark=self.barkp 
     if barkmethod: 
      self.bark=barkmethod 
    def barkp(self): 
     """original bark""" 
     print "woof" 

d=Dog() 
print "calling original bark" 
d.bark() 
print "that was %s\n" % d.bark.__doc__ 

def barknew(): 
    """a new type of bark""" 
    print "wooOOOoof" 

d1=Dog(barknew) 
print "calling the new bark" 
d1.bark() 
print "that was %s\n" % d1.bark.__doc__ 

def barknew1(): 
    """another type of new bark""" 
    print "nowoof" 

d1.bark=barknew1 
print "another new" 
d1.bark() 
print "that was %s\n" % d1.bark.__doc__ 

,輸出是:

calling original bark 
woof 
that was original bark 

calling the new bark 
wooOOOoof 
that was a new type of bark 

another new 
nowoof 
that was another type of new bark 
-4

親愛的這不是重寫你只是與對象調用同一個功能的兩倍。基本上,壓倒一切與多於一個班級有關。當相同的簽名方法存在於不同的類中時,那麼您調用的函數決定了調用此函數的對象。在python中覆寫是可能的,當你讓多個類寫入相同的函數,並且有一件事要分享時,Python中不允許重載。

12

您需要使用類型模塊中的MethodType。 MethodType的用途是覆蓋實例級別的方法(這樣自我可以在覆蓋的方法中使用)。

看下面的例子。

import types 

class Dog: 
    def bark(self): 
     print "WOOF" 

boby = Dog() 
boby.bark() # WOOF 

def _bark(self): 
    print "WoOoOoF!!" 

boby.bark = types.MethodType(_bark, boby) 

boby.bark() # WoOoOoF!! 
1

爲了解釋@ codelogic的出色答卷,我提出了一個更明確的辦法。這與.運算符在將其作爲實例屬性訪問時徹底綁定類方法的方法相同,只不過您的方法實際上是在類之外定義的函數。

使用@ codelogic的代碼,唯一的區別是該方法是如何綁定的。我在Python中使用的函數和方法是非數據descriptors,並且調用了__get__方法。請特別注意,原始文件和替換文件都具有相同的簽名,這意味着您可以將替代文件編寫爲完整的類方法,通過self訪問所有實例屬性。

 
class Dog: 
    def bark(self): 
     print "Woof" 

def new_bark(self): 
    print "Woof Woof" 

foo = Dog() 

# "Woof" 
foo.bark() 

# replace bark with new_bark for this object only 
foo.bark = new_bark.__get__(foo, Dog) 

foo.bark() 
# "Woof Woof" 

通過將綁定方法分配給實例屬性,您已經創建了覆蓋方法的幾乎完整的模擬。缺少的一個便利功能是訪問super的無參數版本,因爲您不在類定義中。另一件事是你的綁定方法的__name__屬性不會像它在類定義中那樣取得它重寫的函數的名字,但你仍然可以手動設置它。第三個區別是你的手動綁定方法是一個純屬性引用,它恰好是一個函數。 .運算符只會獲取該引用。另一方面,當從實例調用常規方法時,綁定過程每次都會創建一個新的綁定方法。

順便說一句,這個工作的唯一原因是實例屬性覆蓋非數據描述符。數據描述符有__set__方法,哪些方法(幸運的是你)沒有。類中的數據描述符實際上優先於任何實例屬性。這就是爲什麼你可以分配給一個屬性:當你嘗試進行分配時,它們的__set__方法會被調用。我個人喜歡更進一步,在實例的__dict__中隱藏底層屬性的實際值,正常情況下,該屬性無法訪問,因爲該屬性會遮蓋它。

您還應該記住這對於magic (double underscore) methods毫無意義。魔術方法當然可以用這種方式重寫,但是使用它們的操作只能看類型。例如,您可以將__contains__設置爲您的實例中的特殊內容,但撥打x in instance將忽略該內容,並使用type(instance).__contains__(instance, x)來代替。這適用於Python data model中指定的所有魔術方法。