我在主项目中有一堂课,我不想改变。
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()
我正在尝试构建一个插件mechansim。到目前为止,我有一个像这样的扩展点:
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()
方法可能非常复杂。换句话说,我无法在self.lastname.upper()
中致电AExtended
。我想调用“超级”方法,它不再存在,因为它已被覆盖。
如何更改代码,以实现以下目标:
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
答案 0 :(得分:15)
这就是我们所说的'装饰'模式。替换名称的原始重新分配,以使其调用函数,而不是原始函数。然后它返回一个新函数。
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
作为当前实例调用self
,并且method
将可用于指向重新分配时该功能的{{1}}
答案 1 :(得分: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'
答案 2 :(得分:2)
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
答案 3 :(得分:1)
在Python等语言中可能并不总是最好的一个选项是使用this non-standard package中的@override
装饰器。但是,只有当您的两个函数处理不同类型或不同数量的参数时,这才是可行的选项。除此之外,除了重命名你的功能之外,你无能为力。
答案 4 :(得分:1)
站在巨人(@spencer)的肩膀上,这是一个更通用的示例。它使一个补丁可以对Original类进行修补,而无需触及其源代码。在此版本中,参数随处可见,并为新方法提供了对任意上下文的引用。
class Original:
def f( self, a, b = None ):
print( "f", a, b )
self.g( b )
def g( self, b ):
print( "g", b )
class Patcher:
def prepare_class( self, clazz ):
@classmethod
def on_class_patcher( cls, func_name, context ):
def patch_by_name( new_func) :
old_func = getattr( cls, func_name )
def patched_func( self, *args, **kwargs ):
return new_func( self, old_func, context, *args, **kwargs )
setattr( cls, func_name, patched_func )
return patch_by_name
setattr( clazz, "patch", on_class_patcher )
class Another:
def log( self, level, info ):
print( level, info )
现在,让我们修补一些东西:
obj = Original()
obj.f( 1, b = "hello" )
p = Patcher()
p.prepare_class( clazz = Original )
logger = Another()
@Original.patch( "f", context = logger )
def new_f( self, old_f, context, a, b ):
print( "new_f", a, b )
context.log( "zzz", a )
old_f( self, a, b )
obj = Original()
obj.f( 1, b = "hello" )
输出为:
f 1 hello
g hello
new_f 1 hello
zzz 1
f 1 hello
g hello