1

I'm trying to decorate class instances with new methods. But if the decorator adds a method from it's own self it gives me an error

TypeError: myMethod() takes 1 positional argument but 2 were given

To give a minimal example, say I have a class MyElement I cannot modify and a Decorator adding a method from itself to instances of MyElement

class MyElement(object):
    def __init(self):
        self._name = "MyElement"

class Decorator(object):

    def myMethod(self):
        print(self._name)

    def decorate(self, element):
        element.myMethod = MethodType(self.myMethod, element)

if __name__ == '__main__':
    d = Decorator()
    p = MyElement()

    d.decorate(p)
    p.myMethod()

This gives me the error above. Altough If I change decorate to this it works :

def decorate(self, element): 
    element.myMethod = MethodType(self.myMethod.__func__, element)

Could somebody explain what actually MethodType is doing? And why the func flag si necessary?

1 Answer 1

2

You're using MethodType to bind your Decorator's myMethod method to another object. This isn't working, because when you access it with self.myMethod, you're already getting a bound method. The first binding passes the Decorator object as self and the second binding causes the exception when it tries to pass the MyElement instance as a second argument.

There are a few ways you could work around this. It's not clear which one would be best for you because your myMethod example doesn't do anything with self.

One option is to access myMethod through the Decorator class, rather than via self. This means it will be unbound (until you wrap it in MethodType of course). In this version, the self values seen in myMethod will be the MyElement instance, not the Decorator (which may be surprising to somebody reading the code). In Python 2, unbound methods had a special check that self was the right type, and so this wouldn't work.

def decorate(self, element):
    element.myMethod = MethodType(Decorator.myMethod, element)

Another option would be to save the bound method you get with self.myMethod as a variable on element without trying to bind it again. With this approach, self will still be the decorator object, even though the method is being called via a MyElement object.

def decorate(self, element):
    element.myMethod = self.myMethod

A final option is to leave the decorate method as it is, but add an extra argument to myMethod. This will allow both the Decorator instance (as self) and the MyElement instance to be passed in:

def myMethod(self, element):
    print("{} is decorated by {}".format(element, self))
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for very accurate answer, now I understant MethodType. element.myMethod = Decorator.myMethod works for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.