Function creation (what happened?)

V

Viktor

Can somebody give me an explanation what happened here (or point me to
some docs)?

Code:

HMMM = None

def w(fn):
print 'fn:', id(fn)
HMMM = fn
print 'HMMM:', id(HMMM)
def wrapper(*v, **kw):
fn(*v, **kw)
wrapper.i = fn
print 'wrapper:', id(wrapper)
return wrapper

class A:
@w
def __init__(self): pass

print 'A.__init__:', id(A.__init__)
print 'A.__init__.i:', id(A.__init__.i)
print 'HMMM:', id(HMMM)



Output:

fn: 10404208
HMMM: 10404208
wrapper: 10404272
A.__init__: 10376136
A.__init__.i: 10404208
HMMM: 505264624



Why did HMMM changed his id?!
 
C

Chris Hulan

Can somebody give me an explanation what happened here (or point me to
some docs)?

Code:

HMMM = None

def w(fn):
print 'fn:', id(fn)
HMMM = fn
print 'HMMM:', id(HMMM)
def wrapper(*v, **kw):
fn(*v, **kw)
wrapper.i = fn
print 'wrapper:', id(wrapper)
return wrapper

class A:
@w
def __init__(self): pass

print 'A.__init__:', id(A.__init__)
print 'A.__init__.i:', id(A.__init__.i)
print 'HMMM:', id(HMMM)

Output:

fn: 10404208
HMMM: 10404208
wrapper: 10404272
A.__init__: 10376136
A.__init__.i: 10404208
HMMM: 505264624

Why did HMMM changed his id?!

The HMMM inside w is local to that function and is not the same HMMM
that is defined just before w.
Have a look at http://docs.python.org/ref/global.html

cheers
 
P

Peter Otten

Viktor said:
Can somebody give me an explanation what happened here (or point me to
some docs)?

Code:

HMMM = None

def w(fn):
print 'fn:', id(fn)
HMMM = fn
print 'HMMM:', id(HMMM)

This prints the id() of the local (to the function w()) HMMM variable
def wrapper(*v, **kw):
fn(*v, **kw)
wrapper.i = fn
print 'wrapper:', id(wrapper)
return wrapper

class A:
@w
def __init__(self): pass

print 'A.__init__:', id(A.__init__)
print 'A.__init__.i:', id(A.__init__.i)
print 'HMMM:', id(HMMM)

while this prints the id() of the global HMMM variable. Python assumes that
a variable is local to a function if there is an assignment to that
variable anywhere inside that function.

If you want to change the variable (Python-lingo "rebind the name") HMMM
declare it as global in the function:

def w(fn):
global HMMM
# ...
HMMM = fn
# ...

Otherwise I've no idea what you are trying to do here...

Peter
 
V

Viktor

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 (I cannot find out if the function is
method, classmethod, staticmethod or just a plain function - 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...

Is there a way to find out in the decorator "what will the decorated
function be"?
 
G

George Sakkis

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 (I cannot find out if the function is
method, classmethod, staticmethod or just a plain function - 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...

Is there a way to find out in the decorator "what will the decorated
function be"?

The decorator does receive the correct function. The problem is that
at this point __init__ is still a plain function, not a method, i.e.
the sequence is:
function -> decorated function -> method

There are several workarounds if you really want to differentiate
between functions and methods, none of them perfect:

- Apply the decorator after the class has been built, i.e. after the
functions have been wrapped in (unbound) methods:
A.__init__ = w(A.__init__)

- (Risky Hack): Guess whether a function is intended to be wrapped in
a method by checking whether its first argument is named "self".
Obviously this is not foolproof and it doesn't work for static/class
methods.

- Have two different decorators, one intended for plain functions and
one for functions-to-be-methods (e.g. @deco, @decomethod).

George
 
V

Viktor

I figured out the first two solutions, but the third looks like the
most cleaner, think I'll use that one...

Thank you everyone. :)
 
G

Gabriel Genellina

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 (I cannot find out if the function is
method, classmethod, staticmethod or just a plain function - tried
with inspect also)... And what is most interesting, when I do:

The decorator receives the original, plain function (unless you chain decorators) and whatever it returns is used instead of the original function.
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...
Is there a way to find out in the decorator "what will the decorated
function be"?

Whatever you return from the decorator.
But the decorator returns a *function* and A.__init__ is a *method*, an instance method in fact. The function can be found as A.__dict__['__init__']. Try this:

m = A.__init__
print m, type(m), id(m)
f = A.__dict__['__init__']
print f, type(f), id(f)

A method combines a function with an instance that becomes the "self" argument. In your case you're building an "unbound" method because it's not tied to a particular instance, but even such unbound method is not the same thing as a plain function (it must ensure that its first argument, when called, is an A instance and not any other object).
The "magic" that converts a simple function into an instance method, for old-style classes like yours, was in the Class type itself. For new style classes, the descriptor protocol is used. See http://www.python.org/doc/newstyle/
 
B

Bruno Desthuilliers

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top