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