2008-12-27 81 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中指定的所有魔术方法。