Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Python

Function creation (what happened?)

 

 

Python python RSS feed   Index | Next | Previous | View Threaded


alefnula at gmail

May 9, 2008, 5:25 AM

Post #1 of 9 (201 views)
Permalink
Function creation (what happened?)

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?!
--
http://mail.python.org/mailman/listinfo/python-list


chris.hulan at gmail

May 9, 2008, 5:38 AM

Post #2 of 9 (191 views)
Permalink
Re: Function creation (what happened?) [In reply to]

On May 9, 8:25 am, Viktor <alefn...@gmail.com> wrote:
> 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
--
http://mail.python.org/mailman/listinfo/python-list


__peter__ at web

May 9, 2008, 5:39 AM

Post #3 of 9 (195 views)
Permalink
Re: Function creation (what happened?) [In reply to]

Viktor wrote:

> 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
--
http://mail.python.org/mailman/listinfo/python-list


duncan.booth at invalid

May 9, 2008, 5:40 AM

Post #4 of 9 (193 views)
Permalink
Re: Function creation (what happened?) [In reply to]

Viktor <alefnula [at] gmail> wrote:

> 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?!

It didn't: global HMMM refers to None both before and after executing
the rest of your code. The other HMMM is local to a particular
invocation of w. Try the same steps interactively (and try printing the
values not just the ids) and it may be more obvious:

>>> HMMM = None
>>> print 'HMMM:', id(HMMM)
HMMM: 505264624
>>> 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


fn: 18299952
HMMM: 18299952
wrapper: 18300016
>>> print 'HMMM:', id(HMMM), HMMM
HMMM: 505264624 None
>>>
--
http://mail.python.org/mailman/listinfo/python-list


alefnula at gmail

May 9, 2008, 6:02 AM

Post #5 of 9 (190 views)
Permalink
Re: Function creation (what happened?) [In reply to]

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"?
--
http://mail.python.org/mailman/listinfo/python-list


george.sakkis at gmail

May 9, 2008, 6:24 AM

Post #6 of 9 (190 views)
Permalink
Re: Function creation (what happened?) [In reply to]

On May 9, 9:02 am, Viktor <alefn...@gmail.com> wrote:
> 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
--
http://mail.python.org/mailman/listinfo/python-list


alefnula at gmail

May 9, 2008, 6:35 AM

Post #7 of 9 (183 views)
Permalink
Re: Function creation (what happened?) [In reply to]

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

Thank you everyone. :)

On May 9, 3:24 pm, George Sakkis <george.sak...@gmail.com> wrote:
> 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

--
http://mail.python.org/mailman/listinfo/python-list


gagsl-py2 at yahoo

May 9, 2008, 7:41 AM

Post #8 of 9 (181 views)
Permalink
Re: Function creation (what happened?) [In reply to]

En Fri, 09 May 2008 10:02:01 -0300, Viktor <alefnula [at] gmail> escribió:

> 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/

--
Gabriel Genellina

--
http://mail.python.org/mailman/listinfo/python-list


bruno.42.desthuilliers at websiteburo

May 9, 2008, 8:12 AM

Post #9 of 9 (182 views)
Permalink
Re: Function creation (what happened?) [In reply to]

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

Python python RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.