performance of dictionary lookup vs. object attributes

86 views
Skip to first unread message

Andre Meyer

unread,
Aug 25, 2006, 4:38:10 AM8/25/06
to pytho...@python.org
Hi all

I was wondering about the performance comparison of either using a
dictionary or an object for a large collection of "things". Therefore
I have run the attached test. I create a dictionary and an object.
Both get the same number of items/attributes, respectively. Then, for
both the values are read often (iterations).

Here are the results:

attributes 100
iterations 1000000
dic 25.515999794
obj 138.570000172

Is the test meaningful and are you surprised by the results? I am,
actually, because I would have assumed that attribute access with an
object should be faster because lookup can be precompiled.

kind regards
Andre

dict_obj.py

bearoph...@lycos.com

unread,
Aug 25, 2006, 8:15:04 AM8/25/06
to
Andre Meyer:

> Is the test meaningful and are you surprised by the results?
> I am, actually, because I would have assumed that attribute access
> with an object should be faster because lookup can be precompiled.

The results seem okay. Python is a dynamic language, object attributes
(and methods, etc) are kept inside a dict, where you can add and remove
them when you like. So using a dict is faster.
You can also take a look at __slots__

Bye,
bearophile

Peter Otten

unread,
Aug 25, 2006, 8:48:00 AM8/25/06
to
Andre Meyer wrote:

I think it is not meaningful as obj.__getattribute__("attribute") is
significantly slower than obj.attribute. dict lookup is still significantly
faster:

$ python -m timeit -s'class A(object): pass' -s 'a = A(); a.alpha = 42'
'a.__getattribute__("alpha")'
1000000 loops, best of 3: 0.674 usec per loop

$ python -m timeit -s'class A(object): pass' -s 'a = A(); a.alpha = 42'
'a.alpha'
1000000 loops, best of 3: 0.215 usec per loop

$ python -m timeit -s'd = dict(alpha=42)' 'd["alpha"]'
10000000 loops, best of 3: 0.167 usec per loop

Peter

Fredrik Lundh

unread,
Aug 25, 2006, 8:52:18 AM8/25/06
to pytho...@python.org
Andre Meyer wrote:

> Is the test meaningful and are you surprised by the results?

surprised by the amount of code you needed to test this, at least. and you
might wish to use the proper spelling for

v = self.obj.__getattribute__(a)

which is

v = getattr(obj, a)

and is quite a bit faster, at least in CPython.

(you should also use setattr() instead of __setattr__; in general, if you find your-
self *calling* a method named __method__, there's most likely a better way to
do it).

> I am, actually, because I would have assumed that attribute access with an
> object should be faster because lookup can be precompiled.

huh? you're using a reflection API; there's no way the compiler can figure out
in advance what you're going to pass to getattr().

</F>

Andre Meyer

unread,
Aug 25, 2006, 9:03:51 AM8/25/06
to Fredrik Lundh, pytho...@python.org
Good points! It's always good to learn from the pros!

So, what it means is that the test is not meaningful, because of the
different way that object attributes are accessed (not as o.x, which
could be compiled).

Nevertheless, the general impression remains that dicts *are* faster
than objects, because attribute lookup uses dicts itself. Is that
correct?

thanks
Andre

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


--
Dr. Andre P. Meyer http://python.openspace.nl/meyer
TNO Defence, Security and Safety http://www.tno.nl/
Delft Cooperation on Intelligent Systems http://www.decis.nl/

Ah, this is obviously some strange usage of the word 'safe' that I
wasn't previously aware of. - Douglas Adams

Fredrik Lundh

unread,
Aug 25, 2006, 9:03:24 AM8/25/06
to pytho...@python.org
Peter Otten wrote:

> $ python -m timeit -s'class A(object): pass' -s 'a = A(); a.alpha = 42'
> 'a.__getattribute__("alpha")'
> 1000000 loops, best of 3: 0.674 usec per loop

also:

timeit -s "class A(object): pass" -s "a = A(); a.alpha = 42"
"a.__getattribute__('alpha')"

1000000 loops, best of 3: 0.592 usec per loop

timeit -s "class A(object): pass" -s "a = A(); a.alpha = 42"

"getattr(a, 'alpha')"
1000000 loops, best of 3: 0.403 usec per loop

which is better, but nowhere close to

timeit -s"class A(object): pass" -s "a = A(); a.alpha = 42" "a.alpha"

10000000 loops, best of 3: 0.158 usec per loop

timeit -s"class A(object): pass" -s "a = A(); a.alpha = 42; d=vars(a)"
"d['alpha']"
10000000 loops, best of 3: 0.126 usec per loop

on the other hand, when we're talking about sub-microsecond operations, you'll
see "massive slowdowns" as soon as you're actually trying to do something with
the data you just fetched...

</F>

Fredrik Lundh

unread,
Aug 25, 2006, 9:24:08 AM8/25/06
to pytho...@python.org
Andre Meyer wrote:

> So, what it means is that the test is not meaningful, because of the
> different way that object attributes are accessed (not as o.x, which
> could be compiled).

correct, but you may be overestimating what the compiler can do. here's
the byte code for the various cases:

# x.attr
2 0 LOAD_FAST 0 (x)
3 LOAD_ATTR 0 (attr)
6 POP_TOP

# getattr(x, "attr")
3 7 LOAD_GLOBAL 1 (getattr)
10 LOAD_FAST 0 (x)
13 LOAD_CONST 1 ('attr')
16 CALL_FUNCTION 2
19 POP_TOP

# x.__getattribute__("attr")
4 20 LOAD_FAST 0 (x)
23 LOAD_ATTR 2 (__getattribute__)
26 LOAD_CONST 1 ('attr')
29 CALL_FUNCTION 1
32 POP_TOP

the LOAD_ATTR instruction uses a much faster code path to get at the
internal dictionary, while the getattr() and __getattribute__ forms needs
to do extra work to get to the actual attribute lookup.

> Nevertheless, the general impression remains that dicts *are* faster
> than objects, because attribute lookup uses dicts itself. Is that
> correct?

correct. once the interpreter gets to actually do the lookup, the performance
is identical (and the dictionary implementation is *extremely fast*). it's the stuff
that needs to be done to get there that differs.

</F>

Aahz

unread,
Aug 25, 2006, 10:34:50 AM8/25/06
to
In article <1156508104.5...@b28g2000cwb.googlegroups.com>,

Taking a look at __slots__ is fine as long as you don't actually use
them.
--
Aahz (aa...@pythoncraft.com) <*> http://www.pythoncraft.com/

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are, by
definition, not smart enough to debug it." --Brian W. Kernighan

Duncan Booth

unread,
Aug 25, 2006, 11:02:05 AM8/25/06
to
Fredrik Lundh wrote:

>> I am, actually, because I would have assumed that attribute access
>> with an object should be faster because lookup can be precompiled.
>
> huh? you're using a reflection API; there's no way the compiler can
> figure out in advance what you're going to pass to getattr().

That isn't necessarily entirely true. You could imagine a situation where
at class definition time the interpreter makes a check for any method which
accesses self.someattr and self is never reassigned in the method, and
someattr is listed in __slots__. It could then create new code for those
methods with (new) bytecodes LOAD_FAST_SLOT/STORE_FAST_SLOT which bypassed
the normal lookup procedures.

Of course it would have to do some fancy footwork to get it right in all
cases (and watch out for people trying to pull the function out of the
class dict for reuse elsewhere), and any speedup might not be noticable,
but it might be worth the experiment.

Gabriel Genellina

unread,
Aug 25, 2006, 11:23:07 AM8/25/06
to Aahz, pytho...@python.org
At Friday 25/8/2006 11:34, Aahz wrote:

> >The results seem okay. Python is a dynamic language, object attributes
> >(and methods, etc) are kept inside a dict, where you can add and remove
> >them when you like. So using a dict is faster.
> >You can also take a look at __slots__
>Taking a look at __slots__ is fine as long as you don't actually use
>them.

Why?


Gabriel Genellina
Softlab SRL





__________________________________________________
Preguntá. Respondé. Descubrí.
Todo lo que querías saber, y lo que ni imaginabas,
está en Yahoo! Respuestas (Beta).
¡Probalo ya!
http://www.yahoo.com.ar/respuestas

sk...@pobox.com

unread,
Aug 25, 2006, 11:33:47 AM8/25/06
to Gabriel Genellina, Aahz, pytho...@python.org

Aahz> Taking a look at __slots__ is fine as long as you don't actually
Aahz> use them.

Gabriel> Why?

http://groups.google.com/group/comp.lang.python/browse_thread/thread/451ad25f9c648404/f4ac2dfde32b16fd?lnk=st&q=Python+__slots__+aahz&rnum=2#f4ac2dfde32b16fd

Skip

bearoph...@lycos.com

unread,
Aug 25, 2006, 1:38:56 PM8/25/06
to
Aahz wrote:
> Taking a look at __slots__ is fine as long as you don't actually use them.

I remember the recent discussion about such matters... but I don't
understand its dangers fully still.
I assume __slots__ may be removed in Python 3.0, but maybe "experts"
need it now an then. Or maybe a "experts only" warning can be added to
the docs.
I think that the official python documentation has to become more like
wikipedia, where people can fix it more easely, so I can add such
warning into the text.
The standard python docs on the Python site can also enjoy a good
amount of usage examples, one example for each thing shown (like in the
really good Delphi manuals).
(I think an unofficial timid example of python docs is being already
recently)

Bye,
bearophile

Fredrik Lundh

unread,
Aug 25, 2006, 6:55:19 PM8/25/06
to pytho...@python.org
bearoph...@lycos.com wrote:

> I think that the official python documentation has to become more like
> wikipedia, where people can fix it more easely, so I can add such
> warning into the text.

comment away:

http://pyref.infogami.com/

</F>

Patrick Maupin

unread,
Aug 26, 2006, 3:51:44 PM8/26/06
to

The subject of __slots__ really seems to get some people's dander up,
to the extent where the heat/light ratio in the discussion becomes
uncomfortably high. Right here, we have Skip referring to a post by
Aahz, where Aahz says that Guido says that slots are bad mojo, without
anybody ever giving concrete examples about why this may be the case.
The only assertion that was made explicitly enough to be testable came
about in a followup to Aahz's original post, only AFTER someone asked
what the side-effects associated with __slots__ were. Aahz responded:

> The main one is that inheritance becomes difficult to nearly-impossible.

But this statement is either wrong or incomplete. Classes can
certainly inherit from other classes which define __slots__, and this
statement doesn't describe the problems well enough for any reader to
figure out whether those problems directly affect him or not. The only
problem I personally know of is that the __slots__ aren't inherited,
but if you just view slots as a storage optimization, that's not a
terrible thing. Also, with duck-typing, inheritance isn't necessarily
all that important for many classes of classes, even if you take the
limited view that all programming is object-oriented.

I can certainly imagine that __slots__ do not give anywhere near the
benefit that was envisioned when they were first implemented, and I can
well believe that the implementation was, in retrospect, a "mistake".
It may well be that, in hindsight, the cost/benefit ratio was too high
to warrant having implemented them.

I can also believe that many people may see __slots__ and think "Aha!
This is how I implement type-checking!" This is typically considered
unPythonic, so obviously when someone posts about how their
type-checking wasn't properly inherited when they subclassed an object,
a lot of heavy signing and gnashing of teeth will ensue.

So, since __slots__ shouldn't have been implemented (and since they may
not be implemented in Python 3000?) and since a lot of question about
__slots__ are in reference to problems they weren't intended to solve
in any case, it makes sense that people get testy about the whole
thing, but is that any reason to _always_ assert that an existing
languange feature should _never_ be used (and never back it up with
relevant concrete examples about the associated problems)?

My own experience with __slots__ has been quite positive, when I wanted
to create millions of instances of the same well-defined small object.
When the choices boil down to using __slots__ or dropping to C or
Pyrex, I'll take __slots__ every time, whether or not that is
Pythonically correct. Now, if someone has a well-written technical
answer about how I might get bitten doing this, I would love to see it
(but "__slots__ might be removed later because it was a mistake, so
you'll have to rewrite your performance code in Pyrex anyway, so you
wasted the 5 minutes you spent adding __slots__ to some of your classes
two years ago" doesn't count). If anybody ever wrote such a useful
answer, I missed it, so please give me a pointer to it!

For a similar, probably better-written, perspective, check out:
http://www.dalkescientific.com/writings/diary/archive/2006/03/19/class_instantiation_performance.html

Regards,
Pat

sk...@pobox.com

unread,
Aug 26, 2006, 4:28:54 PM8/26/06
to Patrick Maupin, pytho...@python.org

Aahz> Taking a look at __slots__ is fine as long as you don't actually
Aahz> use them.

Gabriel> Why?

Skip> http://groups.google.com/group/comp.lang.python/browse_thread/thread/451ad25f9c648404/f4ac2dfde32b16fd?lnk=st&q=Python+__slots__+aahz&rnum=2#f4ac2dfde32b16fd

Patrick> The subject of __slots__ really seems to get some people's
Patrick> dander up, to the extent where the heat/light ratio in the
Patrick> discussion becomes uncomfortably high. Right here, we have
Patrick> Skip referring to a post by Aahz, where Aahz says that Guido
Patrick> says that slots are bad mojo, without anybody ever giving
Patrick> concrete examples about why this may be the case. The only
Patrick> assertion that was made explicitly enough to be testable came
Patrick> about in a followup to Aahz's original post, only AFTER someone
Patrick> asked what the side-effects associated with __slots__ were.
Patrick> Aahz responded:

No dander on my part. I was just pointing out an earlier thread on the
topic. Note however that the ultimate source of the anti-slots fervor in
that thread is Guido himself (may he live long and prosper as BDFL). If
Guido thinks it's bad mojo, that's good enough for me. Also, if he thinks
it's bad mojo now, my suspicion is that you won't see it in Py3k.

That said, It's not mentioned on the Python3.0 page of the wiki:

http://wiki.python.org/moin/Python3.0

or in PEP 3000:

http://www.python.org/dev/peps/pep-3000/

and I see no discussion about it in the Python 3000 mailing list archives:

http://mail.python.org/pipermail/python-3000/

though Ian Bicking asks about it here:

http://wiki.python.org/moin/Python3%2e0Suggestions#head-fc89a0fe3f697418776925f4828ea863031fbbd2

Skip

Jarek Zgoda

unread,
Aug 26, 2006, 4:35:01 PM8/26/06
to
sk...@pobox.com napisał(a):

> That said, It's not mentioned on the Python3.0 page of the wiki:
>
> http://wiki.python.org/moin/Python3.0
>
> or in PEP 3000:
>
> http://www.python.org/dev/peps/pep-3000/
>
> and I see no discussion about it in the Python 3000 mailing list archives:
>
> http://mail.python.org/pipermail/python-3000/
>
> though Ian Bicking asks about it here:
>
> http://wiki.python.org/moin/Python3%2e0Suggestions#head-fc89a0fe3f697418776925f4828ea863031fbbd2

Having that said, should we hope __slots__ would disappear in (some)
future (tomorrow?!, in next 10 microseconds?!)? Please, don't left us
hopeless.

--
Jarek Zgoda
http://jpa.berlios.de/

Patrick Maupin

unread,
Aug 26, 2006, 4:49:25 PM8/26/06
to

I didn't actually sense any dander on your part, so it was probably a
bit unfortunate that I chose to respond to that particular message. I
do (rightly or wrongly) sense some dander on Aahz's part, and this was
the second or third thread where I saw him respond in a similar terse
fashion, but I wasn't really motivated to respond until I saw your
response to his response to, well, you know...

And I know that the BDFL thinks it's a mistake, and he's probably
right. In my previous post, I attempted to rationalize why this is
true. For a start, Guido is probably continually hounded by people
asking stupid __slots__ questions.

Nonetheless, this is one of the few topics on CLP where innocent,
rational questions are more often than not answered with "because I
said so", so I was trying to inject a bit of nuance into the
discussion. (For most similar technical questions, someone will post a
pointer to a post or document which explains in clear technical detail
WHY things are bad, but for this issue there seems to be no comparable
source -- just that rant of the BDFL, which certainly carries a lot of
weight in terms of best practices, but which I find woefully inadequate
in terms of explanations which are dumbed-down enough for me to
follow.)

Also, as I noted, I _do_ use them on occasion, so if there really _are_
potential pitfalls there, I would like to understand exactly what they
are, so my ears perk up whenever I notice a __slots__ discussion, but
so far I have been repeatedly disappointed, in that I always see
someone saying "don't do that" but I have never seen a cogent technical
argument about how my scripts which __slots__ are going to suddenly,
irretrievably break one day.

Regards,
Pat

Patrick Maupin

unread,
Aug 26, 2006, 4:51:17 PM8/26/06
to

Jarek Zgoda wrote:
> Having that said, should we hope __slots__ would disappear in (some)
> future (tomorrow?!, in next 10 microseconds?!)? Please, don't left us
> hopeless.
>

Are you saying you _do_ hope that __slots__ disappear? Why?

Regards,
Pat

Carl Banks

unread,
Aug 26, 2006, 5:16:33 PM8/26/06
to

Patrick Maupin wrote:
> The only assertion that was made explicitly enough to be testable came
> about in a followup to Aahz's original post, only AFTER someone asked
> what the side-effects associated with __slots__ were. Aahz responded:
>
> > The main one is that inheritance becomes difficult to nearly-impossible.
>
> But this statement is either wrong or incomplete.
[snip]

It's definitely an overstatement.

I myself have written a library (Dice3DS, q.v., which is a pure-Python
3DS library), which used both __slots__ and inheritance extensively,
with no problems. I wouldn't even say it was difficult, let alone
nearly impossible, but it definitely required care.

Dice3DS was my learning experience with metaclasses (if you want to
talk about difficult :). I wrote it soon after new-style classes made
their debut, and didn't realize what the intended purpose of __slots__
was. I definitely wouldn't use __slots__ if I were it writing it
today; instead, I'd use a homemade attribute checker that was free from
__slots__'s weird nuances.

In fairness to myself, some 3DS files have thousands or tens of
thousands of chunks, so using __slots__ probably did save quite a bit
of byteage, but that consideration had nothing to do with my decision
to use __slots__.


Carl Banks

Jacob Hallen

unread,
Aug 27, 2006, 7:59:56 PM8/27/06
to
In article <1156625365....@74g2000cwt.googlegroups.com>,

The proper use of__slots__is to save space in objects. Instead of having
a dynamic dict that allows adding attributes to objects at anytime,
there is a static structure which does not allow additions after creation.
This saves the overheadof one dict for every object that uses slots.
While this is sometimes a useful optimisation, it would be completely
unnecessary if the Python interpreter was dynamic enough so that it would
only require the dict when there actually were additions to the object.

Unfortunately there is a side effect to slots. They change the behaviour of
the objects that have slots in a way that can be abused by control freaks
and static typing weenies. This is bad, because the contol freaks should
be abusing the metaclasses and the static typing weenies should be abusing
decorators, since in Python,there should be only one obvious way of doing something.

Making CPython smart enough to handle saving space without __slots__ is a a major
undertaking, which is probably why it is not on the list of changes for P3k (yet).

Jacob Hallén

--

Patrick Maupin

unread,
Aug 27, 2006, 8:52:26 PM8/27/06
to

Jacob Hallen wrote:

> Patrick Maupin <pma...@gmail.com> wrote:
> >Also, as I noted, I _do_ use them on occasion, so if there really _are_
> >potential pitfalls there, I would like to understand exactly what they
> >are, so my ears perk up whenever I notice a __slots__ discussion, but
> >so far I have been repeatedly disappointed, in that I always see
> >someone saying "don't do that" but I have never seen a cogent technical
> >argument about how my scripts which __slots__ are going to suddenly,
> >irretrievably break one day.
>
> The proper use of__slots__is to save space in objects. Instead of having
> a dynamic dict that allows adding attributes to objects at anytime,
> there is a static structure which does not allow additions after creation.
> This saves the overheadof one dict for every object that uses slots.
> While this is sometimes a useful optimisation, it would be completely
> unnecessary if the Python interpreter was dynamic enough so that it would
> only require the dict when there actually were additions to the object.
>
> Unfortunately there is a side effect to slots. They change the behaviour of
> the objects that have slots in a way that can be abused by control freaks
> and static typing weenies. This is bad, because the contol freaks should
> be abusing the metaclasses and the static typing weenies should be abusing
> decorators, since in Python,there should be only one obvious way of doing something.
>
> Making CPython smart enough to handle saving space without __slots__ is a a major
> undertaking, which is probably why it is not on the list of changes for P3k (yet).
>
> Jacob Hallén

Thanks, Jacob!

Saving space is exactly why I use __slots__, so I can sleep better now
:)

Regards,
Pat

David Isaac

unread,
Aug 27, 2006, 11:49:35 PM8/27/06
to

"Jacob Hallen" <ja...@cd.chalmers.se> wrote in message
news:ectbls$ofa$1...@gide.ita.chalmers.se...

> Unfortunately there is a side effect to slots. They change the behaviour
of
> the objects that have slots in a way that can be abused by control freaks
> and static typing weenies. This is bad, because the contol freaks should
> be abusing the metaclasses and the static typing weenies should be abusing
> decorators, since in Python,there should be only one obvious way of doing
something.

Can you please elaborate?
(How to abuse metaclasses and decorators to achieve these effects?
And why is it abuse?)

Thank you,
Alan Isaac


Dieter Maurer

unread,
Aug 28, 2006, 3:26:09 PM8/28/06
to
"Patrick Maupin" <pma...@gmail.com> writes on 26 Aug 2006 12:51:44 -0700:
> ...

> The only
> problem I personally know of is that the __slots__ aren't inherited,

"__slots__" *ARE* inherited, although the rules may be a bit
complex.

>>> class B(object):
... __slots__ = ('f1', 'f2',)
...
>>> class C(B): pass
...
>>> C.__slots__
('f1', 'f2')
>>> c=C()
>>> c.__dict__
{}
>>> c.f1='f1'
>>> c.__dict__
{}
>>> c.fc='fc'
>>> c.__dict__
{'fc': 'fc'}
>>> class C2(B):
... __slots__=('f21',)
...
>>> C2.__slots__
('f21',)
>>> c2=C2()
>>> c2.f1='x'
>>> c2.f21='y'

--
Dieter

Patrick Maupin

unread,
Aug 28, 2006, 8:30:16 PM8/28/06
to

Dieter Maurer wrote:
> "Patrick Maupin" <pma...@gmail.com> writes on 26 Aug 2006 12:51:44 -0700:
> > ...
> > The only
> > problem I personally know of is that the __slots__ aren't inherited,
>
> "__slots__" *ARE* inherited, although the rules may be a bit
> complex.

Yes, I didn't write that correctly. What's not inherited is the lack
of a dictionary :)

>>> class foo(object):
... __slots__ = 'a b c'.split()
...
>>> class bar(foo):
... pass
...
>>> set(dir(foo())) - set(dir(bar()))
set([])
>>> set(dir(bar())) - set(dir(foo()))
set(['__dict__', '__weakref__'])

And you are correct that the rules are a bit complicated, but for my
purposes (memory usage reduction on non-subclassed classes) __slots__
seem to work fine.

Thanks,
Pat

Antoon Pardon

unread,
Aug 29, 2006, 3:07:53 AM8/29/06
to

Your emphasis is wrong.

In Python there should be one obvious way of doing something. Preferably
this is the only obvious way.

--
Antoon Pardon

Reply all
Reply to author
Forward
0 new messages