I have a class in the main project I don't want to change.
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()
I'm trying to build a plugin mechansim. So far so good, I have an extension point like this:
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())
A plugin can be written like this:
class AExtended(A):
''' This is an extension I provide through a plugin mechanism
'''
def name(self):
return self.firstname + ' ' + self.lastname.upper()
This all works very well. I now get "John DOE".
My problem is that the original name()
method can be quite complex. In other words, I can't afford to call self.lastname.upper()
in the AExtended
. I'd like to call the "super" method, which does not exist any more, because it has been overwritten.
How can I change my code, in order to achieve something like this:
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()
Thanks for your help!
Edit: Some explanations of what I try to do.
A
. I can't afford to change existing consumers of AA
that could be changed, I'd like plugins to have full control and responsibilityAExtended
does not have to inherit from A
, but it was an easy way to access self.firstname
. I have no problem following a different design pattern if it can help.I have a workaround, but it's not very elegant and hard to generalize
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
Here's a full example of what I was hinting at. Feel free to yell at me and have me merge my answers, or downvote one or whatever, just easier to provide an alternative as a new answer. I'll let the code do the talking instead of poorly explaining it. :)
## 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'