Question

What's the correct syntax?

Programming attempt

class Foo:
    def hello(self):
        print "Hello cruel world!"
 
    def greet_first(self, f):
        self.hello()
        return lambda *args, **kwargs: f(*args, **kwargs)
 
    @greet_first
    def goodbye(self, concat):
        print "Goodbye {0}".format(concat)
 
if __name__=='__main__':
    bar = Foo()
    bar.goodbye(' and thanks for all the fish')

Debug

Traceback (most recent call last):
  File "prog.py", line 1, in <module>
    class Foo:
  File "prog.py", line 9, in Foo
    @greet_first
TypeError: greet_first() takes exactly 2 arguments (1 given)

Reference

Click to run the code (IDEone)

Was it helpful?

Solution

A decorator is called immediately, it is not treated as a method of Foo but rather is seen as a local function instead. The @greet_first syntax effectively means:

 goodbye = greet_first(goodbye)

and is executed immediately. It is not a bound method, so the self parameter is not included. There is no point in making greet_first a method. Move it out and remove the self argument altogether.

You need to adjust your decorator to return a callable to replace goodbye:

def greet_first(f):
    def wrapper(self, *args, **kwargs):
        self.hello()
        return f(self, *args, **kwargs)
    return wrapper

so that self.hello() is called every time goodbye is called.

If you have to make greet_first part of Foo, you can use a @staticmethod decorator but you have to jump through an extra hoop just to be able to use it for other method declarations; you have to treat it as the descriptor it has become and call .__get__() on it:

class Foo(object):
    def hello(self):
        print "Hello cruel world!"

    @staticmethod
    def greet_first(f):
        def wrapper(self, *args, **kwargs):
            self.hello()
            return f(self, *args, **kwargs)
        return wrapper

    @greet_first.__get__(object)
    def goodbye(self, concat):
        print "Goodbye {0}".format(concat)

I call .__get__() with an arbitrary type (object in this case) because staticmethod ignores that argument anyway; we can't use Foo here because that class has not yet been finalized while inside the code that is part of it's definition.

Note that for @staticmethod to work at all you need to inherit from object in Python 2.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top