如何在猴子补丁后调用原始方法?
我在主项目中有一个类,不想去改动它。
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'