
bruno.42.desthuilliers at websiteburo
May 9, 2008, 8:12 AM
Post #9 of 9
(163 views)
Permalink
|
Viktor a écrit : > This completely slipped of my mind... :) > > I'm trying to change the: > http://wordaligned.org/svn/etc/echo/echo.py > > So if the function is method it prints ClassName.MethodName instead of > MethodName(self|klass|cls=<... ClassName>). > > But it turned out that in the decorator, the wrapped function is > always just a TypeFunction s/TypeFunction/function/ > (I cannot find out if the function is > method, classmethod, staticmethod or just a plain function The latter, unless you decorate it with a classmethod or staticmethod object before. > - tried > with inspect also)... And what is most interesting, when I do: > > def w(fn): > print 'fn:', id(fn) > return fn > > class A: > @w > def __init__(self): pass > > print 'A.__init__:', id(A.__init__) > > It turns out that the function I receive in the wrapper (even when I > return the same function) is not the function which will finally be > attached to the class... Yes it is. But A.__init__ is *not* a function, it's a method. To get at the function, you must use A.__dict__['__init__'] or A.__init__.im_func > Is there a way to find out in the decorator "what will the decorated > function be"? Yes : the decorated function is the function you decorate with the decorator. Ok, this requires some explanations (nb: only valid for new-style classes): - First point : what you declare in the class statement are plain ordinary function. When the class statement is executed - that is, usually[1], at import time - these function objects become attributes of the class object. [1] IOW : if your class statement is at the top-level of your module - Second point: the function class implements the descriptor protocol[2], so when an attribute lookup resolves to a function object, the function's class __get__ method is invoked [2] http://users.rcn.com/python/download/Descriptor.htm - Third point: the function's __get__ method returns a method object, either bound (if lookup was done on an instance) or unbound (if the lookup was done on a class). The function's class __get__ method could be implemented this way: def __get__(self, instance, cls): return types.MethodType(self, instance, cls) - Fourth point: a method object is a thin callable wrapper around the function, the instance (if provided), and the class. It could look like this: class Method(object): def __init__(self, func, instance, cls): self.im_func = func self.im_self = instance self.im_class = cls def __repr__(self): if self.im_self is None: # unbound return "<unbound method %s.%s>" \ % (self.im_class.__name__, self.im_func.__name__) else: # bound return "<bound method %s.%s of %s>" \ % (self.im_class.__name__, self.im_func.__name__, self.im_self) def __call__(self, *args, **kw): if self.im_self is None: try: instance, args = args[0], args[1:] except IndexError: raise TypeError( "unbound method %s() must be called with %s instance " " as first argument (got nothing instead)" \ % (self.im_func.__name__, self.im_class.__name__) if not isinstance(instance, self.im_class): raise TypeError( "unbound method %s() must be called with %s instance " " as first argument (got %s instead)" \ % (self.im_func.__name__, self.im_class.__name__, instance) else: instance = self.im_self return self.im_func(instance, *args, **kw) The classmethod and staticmethod classes (yes, they are classes...) have their own implementation for the descriptor protocol - classmethod.__get__ returns a Method instanciated with func, cls, type(cls), and staticmethod.__get__ returns the original function. So as you can see - and classmethods and staticmethods set aside -, what you decorate is *always* a function. And you just can't tell from within the decorator if this function is called "directly" or from a method object. The only robust solution is to decorate the function with your own custom callable descriptor. Here's a Q&D untested example that should get you started (warning : you'll have to check for classmethods and staticmethods) class wmethod(object): def __init__(self, method): self.method = method def __call__(self, *args, **kw): # called as a method # your tracing code here # NB : you can access the method's # func, class and instance thru # self.method.im_*, return self.method(*args, **kw) class w(object): def __init__(self, func): self.func = func def __get__(self, instance, cls): return wmethod(self.func.__get__(instance, cls)) def __call__(self, *args, **kw): # called as a plain function # your tracing code here return self.func(*args, **kw) HTH -- http://mail.python.org/mailman/listinfo/python-list
|