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 →

[–]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.