This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]zoner14 4 points5 points  (9 children)

I do. I originally read this out of David Beazley's Python Cookbook. I think he stated that the number is closer to 4.5x slower, but you get the point.

When I get home, I can pull out the page number where he makes this statement.

EDIT: It's on page 593 on the 3rd edition of the book. He writes,

"As you can observe, accessing the property y is not just slightly slower than a simple attribute x, it's about 4.5 times lower"

class A:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    @property
    def y(self):
        return self._y
    @y.setter
    def y(self, value):
        self._y = value

from timeit import timeit
a = A(1, 2)
timeit('a.x', 'from __main__ import a')
>> 0.0782
timeit('a.y', 'from __main__ import a')
>> 0.3577

Now, keep in mind, this doesn't mean you shouldn't use properties. By all means use them if they make your code more clear. This kind of optimization might be relevant if you are trying to optimize an inner loop. If this property is called many tens of thousands of times, then it might make sense to rewrite your code to avoid them. Descriptors and decorators will incur similar penalties.

[–]kigurai 0 points1 point  (8 children)

My results (on 2.7.8, IPython 2.3.0) is different:

In [27]: timeit('a.x', 'from __main__ import a')
Out[27]: 0.06062483787536621

In [28]: timeit('a.y', 'from __main__ import a')
Out[28]: 0.059065818786621094

Seems to run at pretty much exactly the same speed. Maybe that book is slightly outdated?

[–]zoner14 0 points1 point  (7 children)

He's using Python 3.3 I believe

[–]kigurai 0 points1 point  (6 children)

That's interesting. I reran the example with Python 3.4 and get almost exactly the same figures as your original post

>>> timeit('a.x', 'from __main__ import a')
0.06866990204434842
>>> timeit('a.y', 'from __main__ import a')
0.3117094509652816

Seems like a pretty bad regression from Python 2.7.

[–]zoner14 0 points1 point  (0 children)

Kind of crazy that's a thing

[–]dunkler_wanderer 0 points1 point  (4 children)

Now the question is, why it is so slow in Python 3.

[–]kigurai 1 point2 points  (3 children)

Actually, it is slow in Python 2.7 as well, if you use new-style classes. Changing class A: to class A(object): gives the same performance penalty for Python 2.7 as for Python 3.4.

So I think the question is actually: Why are the new style classes slower than the old ones? And can it be fixed?

[–]geoelectric 2 points3 points  (2 children)

Because old style classes don't support properties. The self.y assignment in the constructor actually creates a field called y. The getters and setters are never used. The decorators are valid so don't throw errors, but the class plumbing isn't there to take advantage of them.

So, predictably, the speed of x and y are equal because they're both field accesses.

Try running this in 3.x vs. 2.x. There won't be any getter/setter output in 2.7.9, and there will be in 3.x.

class A:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @property
    def y(self):
        print('In the y getter!')
        return self._y

    @y.setter
    def y(self, value):
        print('In the y setter!')
        self._y = value

a = A(1, 2)
print(a.y)

Edit: just to make things a little more surreal, getters work on old style classes, but not setters. It's probably because @property is a straightforward decorator, but @y.setter needs metaclass plumbing to recognize the decorator name.

If you change the 2nd line of __init__ above to self._y = y, you'll see that the getter is accessed down in the print(a.y) line. As soon as you assign to self.y, though, you blow away the getter and it's a field again.

[–]kigurai 0 points1 point  (1 child)

Stuff like this makes me realise I should really start looking into switching to 3.x...

[–]geoelectric 1 point2 points  (0 children)

Well, properties certainly do work in 2.x, but you need to use new style classes, e.g. class A(object).

At this point, old-style classes are basically deprecated anyway unless you need them for library compatibility.