Python中是否有一种方法可以在实例级别覆盖类方法?例如:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Answers:
请不要这样做,如图所示。当您实例化与该类不同的实例时,代码变得不可读。
您无法调试monkeypatched代码。
当您在boby和中发现错误时print 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!!
是的,有可能:
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"
funcType作用和必要性。
funcType = types.MethodType(导入后types)代替来使此操作更简单,更明确funcType = type(Dog.bark)。
__get__该函数将其绑定到实例。
您需要使用 MethodType从types模块。的目的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!!
为了解释@codelogic的出色答案,我提出了一种更为明确的方法。这是与.操作员在将类方法作为实例属性进行访问时完全绑定其方法相同的技术,只是您的方法实际上是在类外部定义的函数。
使用@codelogic的代码,唯一的区别在于方法的绑定方式。我使用函数和方法在Python中是非数据描述符这一事实,并在调用该__get__方法。特别要注意的是,原始和替换都具有相同的签名,这意味着您可以将替换写为完整的类方法,通过访问所有实例属性self。
狗类:
def树皮(个体):
打印“ Woof”
def new_bark():
打印“ Woof Woof”
foo = Dog()
#“天窗”
foo.bark()
#仅将new_bark替换为该对象的树皮
foo.bark = new_bark .__ get __(foo,Dog)
foo.bark()
#“ Woof Woof”
通过将绑定方法分配给实例属性,您已经创建了覆盖方法的几乎完整的模拟。缺少的一个方便的功能是访问的no-arg版本super,因为您不在类定义中。另一件事是,__name__绑定方法的属性不会像在类定义中那样使用要覆盖的函数的名称,但是您仍然可以手动设置它。第三个区别是您的手动绑定方法是一个纯属性引用,恰好是一个函数。该.运营商不只是获取该引用。另一方面,当从实例调用常规方法时,绑定过程每次都会创建一个新的绑定方法。
顺便说一句,这样做的唯一原因是实例属性覆盖了非数据描述符。数据描述符具有__set__方法,而方法(幸运的是您)没有。实际上,该类中的数据描述符优先于任何实例属性。这就是为什么您可以分配一个属性的原因:__set__尝试进行分配时,将调用其方法。我个人更进一步,将实例属性的基础属性的实际值隐藏在实例中__dict__,而正是由于属性遮盖了该属性,因此通常无法使用该值。
您还应该记住,这对于魔术(双下划线)方法毫无意义。魔术方法当然可以用这种方法来覆盖,但是使用它们的操作只看类型。例如,您可以__contains__在实例中设置一些特殊的内容,但是调用x in instance将忽略该内容,type(instance).__contains__(instance, x)而是使用它。这适用于Python数据模型中指定的所有魔术方法。
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如果需要,可以在函数内使用变量。由于您仅针对一个实例对象覆盖方法,因此这种方法更简单,并且具有与using完全相同的效果self。
patching并且这是正确的方法(例如boby = Dog()和boby.bark = new_bark)。在控件的单元测试中,它非常有用。有关更多说明,请参见tryolabs.com/blog/2013/07/05/run-time-method-patching-python(示例)-否我不隶属于链接的网站或作者。
由于functools.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!!
由于函数是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
尽管我喜欢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
我发现这是对原始问题的最准确答案
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()