Main descriptor / decorator
You just need to keep in mind which function you should decorate. Your function is created in __get__ , so it will not help to use the wrapper as a decorator, instead you need to use it in the __get__ method. You can use functools.update_wrapper or decorators.decorator for this. They work very similarly, except that you must save the result of decorators.decorator , while functools.update_wrapper returns None . Both have the signature f(wrapper, wrapped) .
from functools import update_wrapper class class_or_instance(object): def __init__(self, fn): self.fn = fn def __get__(self, obj, cls): if obj is not None: f = lambda *args, **kwds: self.fn(obj, *args, **kwds) else: f = lambda *args, **kwds: self.fn(cls, *args, **kwds)
Now if you do:
print A.func1.__doc__
You will see "some docstring". Hooray!
Cache Properties Keeper
The key point here is that you can only influence what comes back. Since class_or_instance is not actually a function, it really doesn't matter what you do with it. Keep in mind that this method causes the function to bounce every time. I suggest you add a little magic and bind / cache the function after the first call, which is really connected with the addition of the setattr call.
from functools import update_wrapper import types class class_or_instance(object): # having optional func in case is passed something that doesn't have a correct __name__ # (like a lambda function) def __init__(self, name_or_func): self.fn = fn self.name = fn.__name__ def __get__(self, obj, cls): print "GET!!!" if obj is not None: f = lambda *args, **kwds: self.fn(obj, *args, **kwds) update_wrapper(f, self.fn) setattr(obj, self.name, types.MethodType(f, obj, obj.__class__)) else: f = lambda *args, **kwds: self.fn(cls, *args, **kwds) update_wrapper(f, self.fn) return f
And then we can check it ... neato:
A.func1