如何在猴子补丁后调用原始方法?

13 投票
5 回答
6309 浏览
提问于 2025-04-17 09:31

我在主项目中有一个类,不想去改动它。

class A():
    def __init__(self, firstname, lastname):
        self.firstname = firstname
        self.lastname = lastname

    def name(self):
        # this method could be much more complex
        return self.lastname.upper()

我正在尝试建立一个插件机制。到目前为止,一切都很好,我有一个扩展点,像这样:

if __name__ == '__main__':
    ''' The main project has an extension point that allows me to do'''
    # for each class extension such as AExtended:
    A.name = AExtended.name
    ''' After the extensions are loaded, some behaviours may be changed'''
    a = A("John", "Doe")
    print(a.name())

一个插件可以这样写:

class AExtended(A):
    ''' This is an extension I provide through a plugin mechanism
    '''
    def name(self):
        return self.firstname + ' ' + self.lastname.upper()

这一切都运行得很好。我现在得到了“John DOE”。

我的问题是,原来的 name() 方法可能相当复杂。换句话说,我不能在 AExtended 中调用 self.lastname.upper()。我想调用“超类”方法,但它已经被覆盖了,不再存在。

我该如何修改我的代码,以实现类似这样的效果:

class AExtended(A):
    def name(self):
        # I'm looking for a way to call the base implementation that was in A.name()
        return self.firstname + ' ' + parent.name()

谢谢你的帮助!

编辑:我想解释一下我想做的事情。

  • 我希望插件能够修补 A 的行为。我不能改变现有使用 A 的地方
  • 还有很多像 A 这样的类可以被修改,我希望插件能够完全控制和负责
  • 确实 AExtended 不一定要继承自 A,但这样可以方便地访问 self.firstname。如果有其他设计模式能帮助我,我也没问题去尝试。

我有一个变通办法,但它不是很优雅,而且很难推广。

class AExtended(A):
    def name(self):
        # I'm looking for a way to call the base implementation that was in A.name()
        return self.firstname + ' ' + self.parentname()
#in main
A.parentname = A.name
A.name = AExtended.name

5 个回答

2

在编程中,有时候我们会遇到一些问题,比如代码运行不正常或者出现错误。这种时候,我们可以去一些技术论坛,比如StackOverflow,寻求帮助。在这些论坛上,很多人会分享他们的经验和解决方案,帮助其他人解决类似的问题。

在提问的时候,记得把问题描述清楚,包括你遇到的具体情况、错误信息以及你尝试过的解决方法。这样其他人才能更好地理解你的问题,并给出有效的建议。

总之,技术论坛是一个很好的资源,可以帮助我们学习和解决问题。只要我们善于提问和交流,就能从中获得很多有用的信息。

class ABase(object):
    def name(self):
        pass

class A(object):
    pass

class AExtension(ABase):
    def name(self):
        return ABase.name(self)

A.name = AExtension.name
22

这就是我们所说的“装饰器”模式。你可以把原本直接给名字赋值的部分换成调用一个函数,这个函数会接收原来的名字,然后返回一个新的函数。

def name_decorator(method):
    def decorate_name(self=None):
        return stuff + method(self)
    return decorate_name
A.name = name_decorator(A.name)

之后,当你调用 A.name 时,它会用当前的实例去调用 decorate_name,而 method 这个变量在重新赋值的时候会指向那个函数。

6

这里有一个完整的例子,正是我之前提到的内容。你可以随意对我发火,或者让我把我的回答合并,或者给我投反对票,随便你,只是提供一个替代方案作为新的回答对我来说更简单。我会让代码自己说话,而不是用不太好的方式来解释它。 :)

## Some shared class that is used all over the place and needs to be patched.

class A(object):
    def __init__(self):
        self.firstname = 'Bob'

    # Print my first name.
    def name(self):
        return self.firstname

    # Use this to allow patching arbitrary methods...
    @classmethod
    def patch(cls, func_name):
        def patch_by_name(new_func):
            old_func = getattr(cls, func_name)
            def patched_func(self):
                return new_func(self, old_func)
            setattr(cls, func_name, patched_func)
        return patch_by_name

## Some other area of the code where you want to throw in a patch

class PatchedA(A):  # doesn't need to subclass, but comes in handy sometimes
    @A.patch('name')
    def name(self, orig_func):
        return 'I am ' + orig_func(self) + 'McWizwaz'

print 'Who are you, A class?'
print A().name()  # prints 'I am Bob McWizwaz'

撰写回答