覆盖实例级别的方法

时间:2008-12-27 07:12:08

标签: python

Python中有没有办法在实例级别覆盖类方法? 例如:

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

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

11 个答案:

答案 0 :(得分:142)

是的,有可能:

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 :(得分:27)

您需要在类型模块中使用MethodType。 MethodType的目的是覆盖实例级方法(以便在覆盖方法中可以使用self)。

见下面的例子。

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!!

答案 2 :(得分:25)

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具有完全相同的效果。

答案 3 :(得分:18)

为了解释@ 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"

通过将绑定方法分配给实例属性,您已创建了几乎完整的覆盖方法的模拟。缺少一个方便的功能是访问{arner-arg版本的super,因为您不在类定义中。另一件事是绑定方法的__name__属性不会像它在类定义中那样采用它覆盖的函数的名称,但您仍然可以手动设置它。第三个区别是你的手动绑定方法是一个普通的属性引用,恰好是一个函数。 .运算符除了获取该引用外什么都不做。另一方面,当从实例调用常规方法时,绑定过程每次都会创建一个新的绑定方法。

顺便说一下,这种方法的唯一原因是实例属性覆盖非数据描述符。数据描述符有__set__个方法,这些方法(幸运的是)没有。类中的数据描述符实际上优先于任何实例属性。这就是您可以分配给属性的原因:当您尝试进行分配时,会调用它们的__set__方法。我个人希望更进一步,并隐藏实例的__dict__中的基础属性的实际值,在那里它通过正常方式无法访问,因为属性会影响它。

你还应该记住,这对于magic (double underscore) methods来说毫无意义。魔术方法当然可以通过这种方式被覆盖,但使用它们的操作只能查看类型。例如,您可以将__contains__设置为您实例中的特殊内容,但调用x in instance会忽略该内容,而是使用type(instance).__contains__(instance, x)。这适用于Python data model中指定的所有魔术方法。

答案 4 :(得分:14)

请不要如图所示这样做。当您将实例与类不同时,您的代码将变得不可读。

您无法调试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!!

答案 5 :(得分:1)

由于函数是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

答案 6 :(得分:1)

因为这里没有人提到functool.partial

from functools import partial

class Dog:
    name = "aaa"
    def bark(self):
        print("WOOF")

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

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

boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!

答案 7 :(得分:0)

需要在新方法中调用旧方法时要小心:

import types

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

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

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

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

boby.bark() # Process finished with exit code -1073741571 (0xC00000FD) [stack overflow]
# This also happens with the  '__get__' solution

对于这些情况,您可以使用以下方法:

def _bark(self):
  Dog.bark(self)
  print( "WoOoOoF!!") # Calls without error

但是如果库中的其他人已经覆盖了 foobark 方法怎么办?那么 Dog.bark(foo)foo.bark 不一样!根据我的经验,适用于这两种情况的最简单的解决方案是

# Save the previous definition before overriding
old_bark = foo.bark
def _bark(self):
  old_bark()
  print("WoOoOoF!!")
foo.bark = _bark
# Works for instance-overridden methods, too

大多数时候,子类化和使用 super 是处理这种情况的正确方法。但是,有时这种猴子补丁是必要的,除非您更加小心,否则会因堆栈溢出错误而失败。

答案 8 :(得分:-4)

虽然我喜欢S. Lott的继承理念并同意'type(a)'这个东西,但由于函数也有可访问的属性,我认为它可以这样管理:

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

答案 9 :(得分:-4)

亲爱的,这不是重写你只是用对象调用两次相同的函数。基本上覆盖与多个类有关。当相同的签名方法存在于不同的类中时,您调用的函数决定了调用它的对象。当您使多个类写入相同的函数时,可以在python中覆盖,并且在python中不允许分享更多的重载

答案 10 :(得分:-4)

我发现这是原始问题的最准确答案

https://stackoverflow.com/a/10829381/7640677

import a

def _new_print_message(message):
    print "NEW:", message

a.print_message = _new_print_message

import b
b.execute()