[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

getattr(foo, 'foobar') not the same as foo.foobar?

Dave Kuhlman

3/13/2008 10:06:00 PM

The following code has me mystified:

In [4]: class A(object):
...: def show(self):
...: print 'hello'
...:
...:
In [5]: a = A()
In [6]:
In [7]: x = a.show
In [8]: y = getattr(a, 'show')
In [9]: x
Out[9]: <bound method A.show of <__main__.A object at 0xb557d0>>
In [10]: y
Out[10]: <bound method A.show of <__main__.A object at 0xb557d0>>
In [11]:
In [12]: id(x)
Out[12]: 12419552
In [13]: id(y)
Out[13]: 12419872
In [14]:
In [15]: x is y
Out[15]: False
In [16]:
In [17]: x()
hello
In [18]: y()
hello

Basically, the above code is saying that foo.foobar is not the same as
getattr(foo, 'foobar').

But the documentation at
http://docs.python.org/lib/built-in-funcs.h...
says that they are equivalent.

And, the following seems even worse:

>>> id(getattr(a, 'show')) == id(a.show)
True
>>> getattr(a, 'show') is a.show
False

What gives? This breaks my understanding of id(), the is operator, and
getattr().

Can someone help me make sense of this?

I'm using Python 2.5.2.

- Dave

--
Dave Kuhlman
http://www.rexx.com...

15 Answers

Imri Goldberg

3/13/2008 10:31:00 PM

0


Dave Kuhlman wrote:

> The following code has me mystified:
>
> In [4]: class A(object):
> ...: def show(self):
> ...: print 'hello'
> ...:
> ...:
> In [5]: a = A()
> In [6]:
> In [7]: x = a.show
> In [8]: y = getattr(a, 'show')
> In [9]: x
> Out[9]: <bound method A.show of <__main__.A object at 0xb557d0>>
> In [10]: y
> Out[10]: <bound method A.show of <__main__.A object at 0xb557d0>>
> In [11]:
> In [12]: id(x)
> Out[12]: 12419552
> In [13]: id(y)
> Out[13]: 12419872
> In [14]:
> In [15]: x is y
> Out[15]: False
> In [16]:
> In [17]: x()
> hello
> In [18]: y()
> hello
>
> Basically, the above code is saying that foo.foobar is not the same as
> getattr(foo, 'foobar').
>
> But the documentation at
> http://docs.python.org/lib/built-in-funcs.h...
> says that they are equivalent.
>
> And, the following seems even worse:
>
> >>> id(getattr(a, 'show')) == id(a.show)
> True
> >>> getattr(a, 'show') is a.show
> False
>
>
Actually, while I don't know about the basic problem, this doesn't mean
id() is broken.
In the comparison, an object is created, then id() is computed, and the
object is garbage collected.
The same happens to the second object. Since by the time it is created
the first object was garbage collected,
it can have the same id().
As you have already shown in your previous example, when the first
object is not discarded, the id() is different.
Here's some code to illustrate this:

In [17]: bla = [a.foo for x in range(5)]
In [18]: bar = [id(z) for z in bla]
In [19]: bar
Out[19]: [138262924, 137884252, 137884212, 137884452, 137884572]

(This result is equivalent for getattr(a,'foo').)
> What gives? This breaks my understanding of id(), the is operator, and
> getattr().
>
> Can someone help me make sense of this?
>
> I'm using Python 2.5.2.
>
> - Dave
>
>
Cheers,
Imri

-------------------------
Imri Goldberg
www.algorithm.co.il/blogs
www.imri.co.il
-------------------------
Insert Signature Here
-------------------------



Arnaud Delobelle

3/13/2008 10:53:00 PM

0

On Mar 13, 10:06 pm, Dave Kuhlman <dkuhl...@rexx.com> wrote:
> The following code has me mystified:
>
> In [4]: class A(object):
>    ...:     def show(self):
>    ...:         print 'hello'
>    ...:
>    ...:
> In [5]: a = A()
> In [6]:
> In [7]: x = a.show
> In [8]: y = getattr(a, 'show')
> In [9]: x
> Out[9]: <bound method A.show of <__main__.A object at 0xb557d0>>
> In [10]: y
> Out[10]: <bound method A.show of <__main__.A object at 0xb557d0>>
> In [11]:
> In [12]: id(x)
> Out[12]: 12419552
> In [13]: id(y)
> Out[13]: 12419872
> In [14]:
> In [15]: x is y
> Out[15]: False
> In [16]:
> In [17]: x()
> hello
> In [18]: y()
> hello
>
> Basically, the above code is saying that foo.foobar is not the same as
> getattr(foo, 'foobar').
>
> But the documentation athttp://docs.python.org/lib/built-in-funcs.h...
> says that they are equivalent.
>
> And, the following seems even worse:
>
>   >>> id(getattr(a, 'show')) == id(a.show)
>   True
>   >>> getattr(a, 'show') is a.show
>   False
>
> What gives?  This breaks my understanding of id(), the is operator, and
> getattr().
>
> Can someone help me make sense of this?

There are several misconceptions that contribute to your confusion I
think.

1. This has nothing to do with getattr(). If you run the same code
as above, replacing getattr(a, 'show') with a.show you will get the
same results. E.g.

>>> class Foo(object):
... def bar(self): pass
...
>>> id(foo.bar) == id(foo.bar) # (A)
True
>>> foo.bar is foo.bar # (B)
False
>>>

2. The above is because two objects can have the same id if their
lifetimes don't overlap. In (A) by the time the second foo.bar is
created, the first one is already dead. So The second one takes its
place in memory, hence their ids are equal

3. In (B) the first foo.bar is kept alive for comparing with the
second, hence they have a different id.

4. Both points above follow from the fact that foo.bar is really a
function call that returns a (potentially) new object: in fact what
really happens is something like

Foo.__dict__['bar'].__get__(foo, Foo).

So every time foo.bar is executed an object is (or may be) created,
with a new id.

HTH

--
Arnaud

Dave Kuhlman

3/13/2008 11:29:00 PM

0

Arnaud Delobelle wrote:

>
> 4. Both points above follow from the fact that foo.bar is really a
> function call that returns a (potentially) new object: in fact what
> really happens is something like

Arnaud and Imri, too -

No. foo.bar is *not* really a function/method call.

>
> Foo.__dict__['bar'].__get__(foo, Foo).
>
> So every time foo.bar is executed an object is (or may be) created,
> with a new id.
>
> HTH

I appreciate the help, but ...

Actually, it does not help, because ...

My understanding is that foo.bar does *not* create a new object. All it
does is return the value of the bar attribute of object foo. What new
object is being created?

If I have:

class Foo(object):
def bar(self): pass


And I do:

foo = SomeClass()

then:

foo.bar

should return the same (identical) object everytime, no? yes?

I'm still confused.

- Dave

>
> --
> Arnaud

--
Dave Kuhlman
http://www.rexx.com...

Erik Max Francis

3/13/2008 11:31:00 PM

0

Dave Kuhlman wrote:

> Basically, the above code is saying that foo.foobar is not the same as
> getattr(foo, 'foobar').

Python promises that the behavior is the same. It does not promise that
the _objects_ will be the same, which is what `is` determines. That is,
you're not doing a useful test here.

In Python, bound methods are dynamically generated.

--
Erik Max Francis && max@alcyone.com && http://www.alcyon...
San Jose, CA, USA && 37 18 N 121 57 W && AIM, Y!M erikmaxfrancis
There is no present or future; only the past, happening over and over
again, now. -- Eugene O'Neill

Diez B. Roggisch

3/13/2008 11:44:00 PM

0

> My understanding is that foo.bar does *not* create a new object.

Your understanding is not correct.

> All it
> does is return the value of the bar attribute of object foo. What new
> object is being created?

A bound method. This happens through the descriptor-protocol. Please see
this example:


class Foo(object):
def bar(self):
pass


f = Foo()
a = Foo.bar
b = f.bar
c = f.bar

print a, b, c
print id(b), id(c)


The result is this:


<unbound method Foo.bar> <bound method Foo.bar of <__main__.Foo object
at 0xbf650>> <bound method Foo.bar of <__main__.Foo object at 0xbf650>>
315560 788960

So b and c really are different objects - "a is not b == True"

Diez

Aaron Brady

3/13/2008 11:46:00 PM

0

> > Basically, the above code is saying that foo.foobar is not the same as
> > getattr(foo, 'foobar').
>
> > What gives?  This breaks my understanding of id(), the is operator, and
> > getattr().
>
> 4.  Both points above follow from the fact that foo.bar is really a
> function call that returns a (potentially) new object: in fact what
> really happens is something like
>
>     Foo.__dict__['bar'].__get__(foo, Foo).
>
> So every time foo.bar is executed an object is (or may be) created,
> with a new id.

When is it? Why is the 'owner' argument there? Do you ever use
'__set__'?

Arnaud Delobelle

3/13/2008 11:46:00 PM

0

On Mar 13, 11:29 pm, Dave Kuhlman <dkuhl...@rexx.com> wrote:
> Arnaud Delobelle wrote:
>
> > 4.  Both points above follow from the fact that foo.bar is really a
> > function call that returns a (potentially) new object: in fact what
> > really happens is something like
>
> Arnaud and Imri, too -
>
> No.  foo.bar is *not* really a function/method call.

It is. The keyword here is 'descriptor'. Maybe reading this will
help:

http://users.rcn.com/python/download/Desc...

>
>
> >     Foo.__dict__['bar'].__get__(foo, Foo).
>
> > So every time foo.bar is executed an object is (or may be) created,
> > with a new id.
>
> > HTH
>
> I appreciate the help, but ...
>
> Actually, it does not help, because ...
>
> My understanding is that foo.bar does *not* create a new object.  All it
> does is return the value of the bar attribute of object foo.  What new
> object is being created?

A bound method.

Compare the following:

>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x69850>>
>>> Foo.__dict__['bar'].__get__(foo, Foo)
<bound method Foo.bar of <__main__.Foo object at 0x69850>>
>>>

> If I have:
>
>     class Foo(object):
>         def bar(self): pass
>
> And I do:
>
>     foo = SomeClass()
>
> then:
>
>     foo.bar
>
> should return the same (identical) object everytime, no?  yes?

No. This is what I explained in my original reply.

> I'm still confused.

That's because you need to adjust your understanding.

--
Arnaud

Mel

3/14/2008 12:19:00 AM

0

Diez B. Roggisch wrote:
>> My understanding is that foo.bar does *not* create a new object.
>
> Your understanding is not correct.
>
>> All it
>> does is return the value of the bar attribute of object foo. What new
>> object is being created?
>
> A bound method. This happens through the descriptor-protocol. Please see
> this example:
>
>
> class Foo(object):
> def bar(self):
> pass
>
>
> f = Foo()
> a = Foo.bar
> b = f.bar
> c = f.bar
>
> print a, b, c
> print id(b), id(c)

(What Diez said.) From what I've seen, f.bar creates a bound method
object by taking the unbound method Foo.bar and binding its first
parameter with f. This is a run-time operation because it's easy to
re-assign some other function to the name Foo.bar, and if you do, the
behaviour of f.bar() will change accordingly.

You can get some very useful effects from these kinds of games. You
can make f into a file-like object, for example, with

import sys
f.write = sys.stdout.write

Here, f.write *is* a straight attribute of f, although it's a built-in
method of the file class. It's still bound, in a way, to sys.stdout.
I'm assuming that a different example could create an attribute of f
that's a bound method of some other object entirely. I've verified
that f.write('howdy') prints 'howdy' on standard output.

Mel.

Aaron Brady

3/14/2008 12:46:00 AM

0

On Mar 13, 7:18 pm, Mel <mwil...@the-wire.com> wrote:
> Diez B. Roggisch wrote:
> >> My understanding is that foo.bar does *not* create a new object.
>
> > Your understanding is not correct.
>
> >>  All it
> >> does is return the value of the bar attribute of object foo.  What new
> >> object is being created?
>
> > A bound method. This happens through the descriptor-protocol. Please see
> > this example:
>
> > class Foo(object):
> >     def bar(self):
> >         pass
>
> > f = Foo()
> > a = Foo.bar
> > b = f.bar
> > c = f.bar
>
> > print a, b, c
> > print id(b), id(c)
>
> (What Diez said.)  From what I've seen, f.bar creates a bound method
> object by taking the unbound method Foo.bar and binding its first
> parameter with f.  This is a run-time operation because it's easy to
> re-assign some other function to the name Foo.bar, and if you do, the
> behaviour of f.bar() will change accordingly.
>
> You can get some very useful effects from these kinds of games.  You
> can make f into a file-like object, for example, with
>
> import sys
> f.write = sys.stdout.write
>
> Here, f.write *is* a straight attribute of f, although it's a built-in
> method of the file class.  It's still bound, in a way, to sys.stdout.
>   I'm assuming that a different example could create an attribute of f
> that's a bound method of some other object entirely.  I've verified
> that f.write('howdy') prints 'howdy' on standard output.

Accordingly,

f.write= types.MethodType( sys.stdout.__class__.write, sys.stdout ).

It depends on what you want the implicit first (self) to be-- f or
sys.stdout.

But how come this works?

>>> import types
>>> import sys
>>>
>>> class C:
... write= sys.stdout.write
... def g( self ):
... self.write( 'was in \'g\'\n' )
...
>>> c= C()
>>> c.g()
was in 'g'

Shouldn't 'write' be getting extra parameters? Sounds fishy, not to
mix metaphors.

Aaron Brady

3/14/2008 1:15:00 AM

0

On Mar 13, 7:45 pm, castiro...@gmail.com wrote:
> On Mar 13, 7:18 pm, Mel <mwil...@the-wire.com> wrote:
>
>
>
>
>
> > Diez B. Roggisch wrote:
> > >> My understanding is that foo.bar does *not* create a new object.
>
> > > Your understanding is not correct.
>
> > >>  All it
> > >> does is return the value of the bar attribute of object foo.  What new
> > >> object is being created?
>
> > > A bound method. This happens through the descriptor-protocol. Please see
> > > this example:
>
> > > class Foo(object):
> > >     def bar(self):
> > >         pass
>
> > > f = Foo()
> > > a = Foo.bar
> > > b = f.bar
> > > c = f.bar
>
> > > print a, b, c
> > > print id(b), id(c)
>
> > (What Diez said.)  From what I've seen, f.bar creates a bound method
> > object by taking the unbound method Foo.bar and binding its first
> > parameter with f.  This is a run-time operation because it's easy to
> > re-assign some other function to the name Foo.bar, and if you do, the
> > behaviour of f.bar() will change accordingly.
>
> > You can get some very useful effects from these kinds of games.  You
> > can make f into a file-like object, for example, with
>
> > import sys
> > f.write = sys.stdout.write
>
> > Here, f.write *is* a straight attribute of f, although it's a built-in
> > method of the file class.  It's still bound, in a way, to sys.stdout.
> >   I'm assuming that a different example could create an attribute of f
> > that's a bound method of some other object entirely.  I've verified
> > that f.write('howdy') prints 'howdy' on standard output.
>
> Accordingly,
>
> f.write= types.MethodType( sys.stdout.__class__.write, sys.stdout ).
>
> It depends on what you want the implicit first (self) to be-- f or
> sys.stdout.
>
> But how come this works?
>
> >>> import types
> >>> import sys
>
> >>> class C:
>
> ...     write= sys.stdout.write
> ...     def g( self ):
> ...             self.write( 'was in \'g\'\n' )
> ...>>> c= C()
> >>> c.g()
>
> was in 'g'
>
> Shouldn't 'write' be getting extra parameters?  Sounds fishy, not to
> mix metaphors.

Ah. Because this doesn't.

>>> class C:
... write= sys.stdout.__class__.write #<--
... def g( self ):
... self.write( 'was in \'g\'\n' )
...
>>> c= C()
>>> c.g()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in g
File "c:\programs\python\lib\io.py", line 1236, in write
if self.closed:
AttributeError: 'C' object has no attribute 'closed'
>>>

That is, because sys.stdout.write is -not- a user-defined function.
What it is, is a bound member function, and only the former is
converted/wrapped/bound*, as it is in the subsequent example.

*/ whatever.