dmzkrsk
dmzkrsk

Reputation: 2115

Python: decorate __call__ and ordinary function with the same decorator?

Is it possible in Python 2.6-2.7 to use the same decorator for the following task:

class ComplextCallableObject(object):
    @wrap_me
    def __call__(self, a1, a2):
        pass

@wrap_me
def simple_function(a1, a2):
    pass

Both ComplextCallableObject.__call__ and simple_function has the same args, but __call__ also has self for the first arg. In the wrap_me decorator I need an access for the function being wrapped args.

Upvotes: 4

Views: 613

Answers (3)

Lev Levitsky
Lev Levitsky

Reputation: 65811

This may be pretty dumb, but might as well work for the simplest case:

In [1]: def wrap_me(func):
   ...:     def wrapped(*args):
   ...:         print 'arg1 is', args[-2]
   ...:         print 'arg2 is', args[-1]
   ...:         func(*args)
   ...:     return wrapped
   ...:
In [2]: class ComplexCallableObject(object):
   ...:     @wrap_me
   ...:     def __call__(self, a1, a2):
   ...:         print 'class object called'
   ...:
In [3]: @wrap_me
   ...: def simple_function(a1, a2):
   ...:     print 'function called'
   ...:
In [4]: simple_function('A', 'B')
arg1 is A
arg2 is B
function called
In [5]: o = ComplexCallableObject()
In [6]: o('A', 'B')
arg1 is A
arg2 is B
class object called

Upvotes: 0

Shane Holloway
Shane Holloway

Reputation: 7852

Unfortunately, at the time of definition (the class block in this case), the code cannot tell how a function will be used except by naming convention. Modifying your example a bit:

class ComplextCallableObject(object):
    @wrap_me
    def __call__(self, a1, a2):
        pass #...

@wrap_me
def simple_function(tgt, a1, a2):
    pass

ComplextCallableObject.anInstanceMethod = simple_function
ComplextCallableObject.anClassMethod = classmethod(simple_function)
ComplextCallableObject.aStaticMethod = staticmethod(simple_function)

In this case, simple_function is implementing a function taking a target and two parameters, an instance method taking two parameters, a class method taking two parameters, and a static method taking a target and two parameters. But these uses are not bound until after the function is defined. Both staticmethod and classmethod return a different object type, so you can tell those apart, if need be.

If you did want to use convention, you could inspect the function's first argument name to see if it is self:

def wrap_me(fn):
    names = fn.func_code.co_varnames
    if names and names[0]=='self':
        print 'looks like an instance method'
    else: print 'looks like a function'
    return fn

Upvotes: 2

Katriel
Katriel

Reputation: 123722

def wrap_me(*args):
    a1, a2 = args if len(args) == 2 else args[1:]
    ...

Of course, you will need to modify the returned function to take the self argument if wrap_me was called on a class method as well.

By the way, assuming you're not actually using self inside the function which you're decorating, it should really be a static method.

Upvotes: 0

Related Questions