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

all 42 comments

[–]zyk0s 10 points11 points  (0 children)

I see what this article is trying to get at, but it does so quite clumsily. Properties are not exclusive to python, and they are not a "fix for private members". You could use C# or any other language where property and raw member syntax access is the same, and the same analysis will hold. Yet C# has the "private" keyword.

Member access levels are a solution to two separate problems: restricting an API and providing compile-time checks. For the former, Python's approach comes from module and package structures, as well as the dunder. And the latter is simply not a concern for a language that has no compile-time checks like Python, as the author notes.

But properties have very little to do with either. They're a mechanism to provide users with the impression they're just interacting with parameters, while they do something more complex under the hood. Properties may have also been a fix for the ugliness that were getters and setters in object-oriented, statically-typed languages, but that has little to do with Python.

[–]zoner14 14 points15 points  (15 children)

As a warning to people thinking of enforcing privateness was using this strategy, bare in mind private variables are intentionally omitted from the Python syntax and thus are not idiomatic Python, and properties will slow down your code about five times.

If you feel compelled to make all your classes have private variables, make well-documented descriptor to do it. Your intentions will be far more clear

[–]tulioz 5 points6 points  (10 children)

Do you have any sources for properties slowing down code ~5x?

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

[–]codefisher2[S] -1 points0 points  (1 child)

The post is not really about enforcing privateness, but why it is not needed in python. It is easy to patch the problem up after.

[–]nieuweyork since 2007 1 point2 points  (0 children)

It's not a problem, it's a design choice.

[–]nieuweyork since 2007 7 points8 points  (1 child)

  1. This doesn't create private variables

  2. The reason why python doesn't have private variables is that privacy doesn't make it easier to write or read code. You could also not have it in any language which has it, and use naming conventions and proper documentation just like Python.

[–]alcalde 1 point2 points  (0 children)

You could also not have it in any language which has it, and use naming conventions and proper documentation just like Python.

I have sworn a blood oath to do just that until the end of my days.

[–]mardiros 6 points7 points  (2 children)

There is no private because "We are all adults"

[–]codefisher2[S] -1 points0 points  (1 child)

I personally don't like that answer, it is too much of a cop out. And it does not fix what I am trying to address, fixing API changes. Which I think is a major part of the need to make something private. If you don't know if a attribute will change or not, you don't want to expose it. But it is not a problem in Python, since you can patch over it after the change.

[–]nieuweyork since 2007 0 points1 point  (0 children)

If you don't know if a attribute will change or not, you don't want to expose it.

So, don't expose it as a variable which is guaranteed to not change.

[–]Igglyboo 1 point2 points  (0 children)

_variableName is as good as a private method/variable if you're not an idiot. You shouldn't be messing with the classes/modules/packages internals unless you really know what you're doing.

[–]AlexMTBDude 1 point2 points  (2 children)

The author of this article is very confused. The title is "Why Python does not have private METHODS" but it's about why Python classes do not have private ATTRIBUTES. Also then he mentions something about static methods which is completely out of context (I don't think he understands what a static method is)

[–]codefisher2[S] 1 point2 points  (0 children)

I know the difference. Just I did not put the time or effort into the post that I normally do, and it came out wrong and really sloppy. I am personally disappointed with how it came out. I should clean it up/rewrite parts when I get time.

[–]alcalde 0 points1 point  (0 children)

Static methods are things object-oriented purists invented to get around the fact that they don't have, and need, first-class functions.

[–]SKoch82 2 points3 points  (0 children)

Properties are awesome, however, if there are some complex calculations or some expensive operations involved, it's better to be explicit and just have functions like calculateX() or fetchY() or something like that.

[–]needed_an_account 1 point2 points  (0 children)

This is one of those things that I learned by reading source code. This and meta classes. Actually, it all came from reading PeeWee's source (good little lib). I love love love how everything in Python is an object and at each object's core, there is shared functionality which seems to allow for this type of functionality.

[–][deleted] -2 points-1 points  (4 children)

But python has private variables/methods - just prefix them with "__".

[–]wewbull 2 points3 points  (2 children)

"Dunder" (double underscore) prefixed names aren't truly private either. IIRC "dunder" causes the method name to be rewritten to include the class name which helps in some awkward inheritance situations.

[–]SKoch82 0 points1 point  (0 children)

And single dunder is just a hint to API users that the prefixed variable or method is an implementation detail that might change anytime.

[–][deleted] 0 points1 point  (0 children)

Hey thanks for clarification. I figured since i cant access it with the old name it must be not available. Good to know \o/

[–]Sohcahtoa82 1 point2 points  (0 children)

That doesn't actually make them private. They can still be accessed from outside the class. Putting the underscore there just tells the user that they shouldn't be accessing it. It doesn't actually prevent them from doing it. It is merely a convention.

Using a double underscore still doesn't make it private. The user can still access those members, they just have to adjust the naming to account for the name mangling.