Yuras
Yuras

Reputation: 482

How to distinguish between function and a method in decorator

I am trying to write decorator that will do different job depending on that whether it will be decorating method or a function.

I tried something like this:

def dec(f):
    def wrapper(*v, **kv):
        if '__class__' in dir(f):
            text = "Its a method"
        else:
            text = "Its a function"
        print(text)
        return f(*v, **kv)
    return wrapper


class A:

    @dec
    def f(self):
        pass

@dec
def f():
    pass

a = A()

a.f()
f()

It returns:

Its a method
Its a method

But this won't work since all functions also have also __class__ attribute :) I tried also to do inspect.ismethod on A.f but A.f is treaten just like function but in A namespace and it return of course false. It would return True if we have done inspect.ismethod(a.f) where a is instance of A but I can't wait to have instance.

Upvotes: 0

Views: 81

Answers (1)

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

As Martijn already explained in his comments, being defined in a class statement's body doesn't make a function a method - the method object is created at lookup time, as explained here - so what you are decorating is and will always (well almost cf below) be a function. FWIW, Python's "methods" are just thin wrappers around the function, class and (eventually) instance.

The only case where you might get something else than a function are classmethods and staticmethods (in which case you'll get resp. a classmethod or staticmethod object instead), and even then it depends on the decorators order.

If you want to get at the method's current instance (or class) in your decorator, you know that it will always be the first positional argument - but this might not be of great help... To make a long story short, you have to either explicitely tell your decorator if it should treat the function as plain function or method, or decorate it with a custom descriptor in which case you'll know if the function has - or not - been looked up as a method... A naïve implementation migh look like this:

class MyDecorator(object):
    def __init__(self, func, instance=None):
        self.func = func
        self.instance = instance

    def __call__(self, *args, **kw):
        if self.instance:
            print "is method - instance : %s" % self.instance
            args = (self.instance,) + args
        else:
            print "is_function"

        return self.func(*args, **kw)

    def __get__(self, instance, cls):
        return type(self)(self.func, instance)

Upvotes: 1

Related Questions