473,398 Members | 2,368 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,398 software developers and data experts.

Unexpected behaviour of getattr(obj, __dict__)

I came across this unexpected behaviour of getattr for new style classes.
Example:
class Parrot(object): .... thing = [1,2,3]
.... getattr(Parrot, "thing") is Parrot.thing True getattr(Parrot, "__dict__") is Parrot.__dict__

False

I would have expected that the object returned by getattr would be the
same object as the object returned by standard attribute access. This is
true for some attributes, but not for __dict__. I don't know if there are
other examples.

Why is this? Is there some documentation I can read up about this? I've
tried searching, but can't find anything useful.

--
Steven.

Feb 14 '06 #1
8 1880
Steven D'Aprano wrote:
I came across this unexpected behaviour of getattr for new style classes.
Example:

class Parrot(object):
... thing = [1,2,3]
...
getattr(Parrot, "thing") is Parrot.thing
True
getattr(Parrot, "__dict__") is Parrot.__dict__
False

hint:
getattr(object, '__dict__')

<dictproxy object at 0x2aaaaab2ff30>
--
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'o****@xiludom.gro'.split('@')])"
Feb 14 '06 #2
Steven D'Aprano wrote:
I came across this unexpected behaviour of getattr for new style classes.
Example:
class Parrot(object): ... thing = [1,2,3]
... getattr(Parrot, "thing") is Parrot.thing True getattr(Parrot, "__dict__") is Parrot.__dict__ False

I would have expected that the object returned by getattr would be the
same object as the object returned by standard attribute access.


The returned object is a wrapper created on-the-fly as needed. You've
requested two of them and each wrapper has a different object id but
wraps an identical source.

Contemplate this for a bit:
class Parrot(object): thing = [1,2,3]
def f(self): pass
getattr(Parrot, 'f') is getattr(Parrot, 'f') False getattr(Parrot, '__dict__') is getattr(Parrot, '__dict__') False
The reason why is evident when you check the object representation. It
shows that your lookup returned a wrapper:
getattr(Parrot, 'f') # creates a function wrapper <unbound method Parrot.f> getattr(Parrot, '__dict__') # creates a dictionary wrapper

<dictproxy object at 0x00C41770>

IOW, attribute lookup can do more than just return the result of a
straight-lookup. The underlying mechanism is a deep and interesting
subject. If you want to know more, try this link:

http://users.rcn.com/python/download/Descriptor.htm

Raymond

Feb 14 '06 #3
On Tue, 14 Feb 2006 13:03:17 +0100, bruno at modulix wrote:
Steven D'Aprano wrote:
I came across this unexpected behaviour of getattr for new style classes.
Example:

>class Parrot(object):


... thing = [1,2,3]
...
>getattr(Parrot, "thing") is Parrot.thing


True
>getattr(Parrot, "__dict__") is Parrot.__dict__


False

hint:
getattr(object, '__dict__')

<dictproxy object at 0x2aaaaab2ff30>


That doesn't answer the question, it just re-words it. Why is the
dictproxy returned by getattr a different instance from the dictproxy that
you get when you say object.__dict__?

--
Steven.

Feb 14 '06 #4
On Tue, 14 Feb 2006 04:11:52 -0800, Raymond Hettinger wrote:
Steven D'Aprano wrote:
I came across this unexpected behaviour of getattr for new style classes.
Example:
>>> class Parrot(object):

... thing = [1,2,3]
...
>>> getattr(Parrot, "thing") is Parrot.thing

True
>>> getattr(Parrot, "__dict__") is Parrot.__dict__

False

I would have expected that the object returned by getattr would be the
same object as the object returned by standard attribute access.


The returned object is a wrapper created on-the-fly as needed. You've
requested two of them and each wrapper has a different object id but
wraps an identical source.


[penny drops]

That would certainly explain it.

Is there a canonical list of attributes which are wrapped in this way? I
suppose not... it is probably the sort of thing which is subject to change
as Python evolves.

I knew methods were wrapped, but I didn't know they were wrapped on the
fly, nor did I connect the two phenomena. Thank you for the concise and
simple answer.
--
Steven.

Feb 14 '06 #5
Raymond Hettinger wrote:
Steven D'Aprano wrote:
I came across this unexpected behaviour of getattr for new style classes.
Example:
>> class Parrot(object):

... thing = [1,2,3]
...
>> getattr(Parrot, "thing") is Parrot.thing

True
>> getattr(Parrot, "__dict__") is Parrot.__dict__

False

I would have expected that the object returned by getattr would be the
same object as the object returned by standard attribute access.


The returned object is a wrapper created on-the-fly as needed. You've
requested two of them and each wrapper has a different object id but
wraps an identical source.

Contemplate this for a bit:
class Parrot(object): thing = [1,2,3]
def f(self): pass
getattr(Parrot, 'f') is getattr(Parrot, 'f') False getattr(Parrot, '__dict__') is getattr(Parrot, '__dict__') False

Yes, in fact getattr has nothing to do with it. To wit:
Parrot.f is Parrot.f False Parrot.__dict__ is Parrot.__dict__ False

But wait, it gets weirder.
id(Parrot.f) == id(Parrot.f) True id(Parrot.__dict__) == id(Parrot.__dict__) True

But that's not all. Redefine Parrot as:

class Parrot(object):
def f(self): pass
def g(self): pass

Then,
id(Parrot.f) == id(Parrot.g)

True
your-milage-may-vary-ly yr's,

Carl Banks

Feb 14 '06 #6
Steven D'Aprano wrote:
>>class Parrot(object):

... thing = [1,2,3]
...

>>getattr(Parrot, "thing") is Parrot.thing

True

>>getattr(Parrot, "__dict__") is Parrot.__dict__

False

hint:
> getattr(object, '__dict__')

<dictproxy object at 0x2aaaaab2ff30>


That doesn't answer the question, it just re-words it. Why is the
dictproxy returned by getattr a different instance from the dictproxy
that you get when you say object.__dict__?


because it's created on the fly:
Parrot.__dict__ <dictproxy object at 0x009818B0> Parrot.__dict__ <dictproxy object at 0x00981A10> Parrot.__dict__ <dictproxy object at 0x009818B0> Parrot.__dict__ <dictproxy object at 0x00981A10> Parrot.__dict__ <dictproxy object at 0x009818B0> Parrot.__dict__ <dictproxy object at 0x00981A10> Parrot.__dict__ <dictproxy object at 0x009818B0> Parrot.__dict__ <dictproxy object at 0x00981A10> Parrot.__dict__ <dictproxy object at 0x009818B0> Parrot.__dict__ <dictproxy object at 0x00981A10> Parrot.__dict__

<dictproxy object at 0x009818B0>

the object itself contains a dictionary.

</F>

Feb 14 '06 #7

"Carl Banks" <in**********@aerojockey.com> wrote in message
news:11*********************@g44g2000cwa.googlegro ups.com...
But wait, it gets weirder.
Not weird at all. Just an artifact of CPython's storage recycling
algorithm.
id(Parrot.f) == id(Parrot.f) True id(Parrot.__dict__) == id(Parrot.__dict__) True


A wrapper is created and passed to id() which returns an int object while
releasing the wrapper back to the free list. Then another wrapper is
created in the same chunk of memory and passed to id, which returns an int
of the same value for comparison. The language ref only guarantees
uniqueness of ids at any particular instant and allows reuse of ids of
deallocated objects.

I half seriously think the lib ref entry for id() should have a warning
that naive use can mislead.
But that's not all. Redefine Parrot as:

class Parrot(object):
def f(self): pass
def g(self): pass

Then,
id(Parrot.f) == id(Parrot.g)

True


Same thing. The wrapper content is not relevant as long as it uses the
same memory block.

Terry Jan Reedy

Feb 15 '06 #8
On Tue, 14 Feb 2006 22:24:12 -0500, rumours say that "Terry Reedy"
<tj*****@udel.edu> might have written:
> id(Parrot.f) == id(Parrot.f)

True
> id(Parrot.__dict__) == id(Parrot.__dict__)

True


A wrapper is created and passed to id() which returns an int object while
releasing the wrapper back to the free list. Then another wrapper is
created in the same chunk of memory and passed to id, which returns an int
of the same value for comparison. The language ref only guarantees
uniqueness of ids at any particular instant and allows reuse of ids of
deallocated objects.

I half seriously think the lib ref entry for id() should have a warning
that naive use can mislead.


Actually, you more-or-less just wrote what could (I also think should) be
included in the docs.
--
TZOTZIOY, I speak England very best.
"Dear Paul,
please stop spamming us."
The Corinthians
Feb 16 '06 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Ed Young | last post by:
Here is an example of the behavior: ------- code start ----------------------------------- #!/usr/bin/python #bugtest - test of class attribute initiation class Config: a = 1 b = 2 c = 3
90
by: Mark Hahn | last post by:
"Michael Geary" <Mike@Geary.com> wrote ... >Does anyone have some sample code where obj$func() would be used? > (Apologies if I missed it.) There have been so many messages about delegation...
7
by: Chris | last post by:
hello, I have question about the re.I option for Regular Expressions: >>> import re >>> re.findall('x', '1x2X3', re.I) as expected finds both lower and uppercase x
13
by: Pierre | last post by:
Hi, Sorry in advance, english is not my main language :/ I'd like to customize the result obtained by getattr on an object : if the object has the requested property then return it BUT if the...
2
by: indiarocks | last post by:
Just a basic question .... When using getattr for a specific method from a different class, can we somehow tell getattr to do a case-insensitive search. Say for eg. m =...
12
by: Ivan Voras | last post by:
While using PyGTK, I want to try and define signal handlers automagically, without explicitly writing the long dictionary (i.e. I want to use signal_autoconnect()). To do this, I need something...
3
by: Rotlaus | last post by:
Hello, lets assume i have some classes: class A(object): def __init__(self): b = B() class B(object): def __init__(self):
8
by: Gregor Horvath | last post by:
Hi, class A(object): test = "test" class B(object): a = A() In : B.a.test
4
by: maestro | last post by:
Why are these functions there? Is it somehow more idiomatic to use than to do obj.field ? Is there something you can with them that you can't by obj.field reference?
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.