在实例级别重写方法

2025-03-05 09:18:00
admin
原创
77
摘要:问题描述:Python 中是否有办法在实例级别覆盖类方法?例如:class Dog: def bark(self): print "WOOF" boby = Dog() boby.bark() # WOOF # METHOD OVERRIDE boby.bark(...

问题描述:

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

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

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

解决方案 1:

是的,这是可能的:

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"

解决方案 2:

您需要使用模块中的MethodTypetypes。其目的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!!

解决方案 3:

为了解释@codelogic的出色答案,我提出了一种更明确的方法。这与.运算符在将类方法作为实例属性访问时彻底绑定类方法的技术相同,只是您的方法实际上是在类之外定义的函数。

使用 @codelogic 的代码,唯一的区别在于方法的绑定方式。我利用函数和方法在 Python 中是非数据描述符__get__这一事实,并调用该方法。特别注意,原始方法和替换方法具有相同的签名,这意味着您可以将替换方法编写为完整的类方法,通过 访问所有实例属性self

狗类:
    def bark(自身):
        打印“Woof”

def new_bark(自身):
    打印“汪汪”

foo = 狗()

# “汪汪”
foo.bark()

# 仅为此对象将 bark 替换为 new_bark
foo.bark = new_bark.__get__(foo,狗)

foo.bark()
#“汪汪”

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

顺便说一句,这种方法奏效的唯一原因是实例属性会覆盖非数据描述符。数据描述符具有__set__方法,而方法(对您来说很幸运)没有。类中的数据描述符实际上优先于任何实例属性。这就是您可以分配给属性的原因:__set__当您尝试进行分配时,将调用它们的方法。我个人喜欢更进一步,将底层属性的实际值隐藏在实例中__dict__,由于属性遮蔽了它,因此无法通过正常方式访问它。

您还应该记住,这对于魔术(双下划线)方法毫无意义。魔术方法当然可以通过这种方式覆盖,但使用它们的操作只会查看类型。例如,您可以__contains__在实例中设置为某些特殊值,但调用x in instance会忽略该值并type(instance).__contains__(instance, x)改为使用。这适用于 Python数据模型中指定的所有魔术方法。

解决方案 4:

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

解决方案 5:

请不要这样做。当你用 monkeypatch 使实例与类不同时,你的代码将变得不可读。

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

解决方案 6:

由于这里没有人提及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!!

解决方案 7:

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

解决方案 8:

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

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是处理这种情况的正确方法。但是,有时这种 monkeypatching 是必要的,并且除非您更加小心,否则将因堆栈溢出错误而失败。

解决方案 9:

如果您尝试“包装”基本方法,您将收到{RecursionError}maximum recursion depth exceeded in comparison错误。

为了解决这个问题self.__class__.method,使用self.method


In [21]: import types
    ...: 
    ...: 
    ...: class Dog:
    ...:     def bark(self):
    ...:         print("WOOF")
    ...: 
    ...: 
    ...: def my_bark(self):
    ...:     print("RRRR")
    ...:     self.__class__.bark(self)
    ...: 
    ...: 
    ...: dog = Dog()
    ...: dog.bark = types.MethodType(my_bark, dog)
    ...: dog.bark()
    ...: 
RRRR
WOOF

解决方案 10:

Python 3 解决方案

其他答案仍然使用 Python 2。以下是更新的解决方案:

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

my_dog = Dog()

# "WOOF"
my_dog.bark()

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

# Replace bark() with new_bark() for my_dog only
my_dog.bark = new_bark.__get__(my_dog, Dog)

# "WoOoOoF!!"
my_dog.bark()

请注意,这不适用于魔术方法。为此,您需要一个包装器类。

解决方案 11:

虽然我喜欢 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
" % 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
" % d1.bark.__doc__

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

d1.bark=barknew1
print "another new"
d1.bark()
print "that was %s
" % 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

解决方案 12:

亲爱的,这不是覆盖,你只是用对象调用了两次相同的函数。基本上,覆盖与多个类有关。当不同的类中存在相同的签名方法时,你调用哪个函数将决定调用这个函数的对象。当你让多个类编写相同的函数时,在 python 中可以进行覆盖,还有一件事要分享,那就是在 python 中不允许重载

解决方案 13:

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

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()
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2599  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1556  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。其中,技术评审与决策评审是IPD流程中至关重要的环节,它们既有明显的区别,又存在紧密的协同关系。深入理解这两者的区别与协同,对于企业有效实施IPD流程,提升产品开发效率与质量具有重要意义...
IPD管理流程   38  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、ClickUp、Freshdesk、GanttPRO、Planview、Smartsheet、Asana、Nifty、HubPlanner、Teamwork。在当今快速变化的商业环境中,项目管理软件已成为企业提升效率、优化资源分配和确保项目按时交付的关键工具。然而...
项目管理系统   32  
  建设工程项目质量关乎社会公众的生命财产安全,也影响着企业的声誉和可持续发展。高质量的建设工程不仅能为使用者提供舒适、安全的环境,还能提升城市形象,推动经济的健康发展。在实际的项目操作中,诸多因素会对工程质量产生影响,从规划设计到施工建设,再到后期的验收维护,每一个环节都至关重要。因此,探寻并运用有效的方法来提升建设工程...
工程项目管理制度   28  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用