Reputation: 482
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
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