"public" or "private" attribute in Python ? What is the best way?
Typically, Python code strives to adhere to the Uniform Access Principle. Specifically, the accepted approach is:
- Expose your instance variables directly, allowing, for instance,
foo.x = 0
, notfoo.set_x(0)
- If you need to wrap the accesses inside methods, for whatever reason, use
@property
, which preserves the access semantics. That is,foo.x = 0
now invokesfoo.set_x(0)
.
The main advantage to this approach is that the caller gets to do this:
foo.x += 1
even though the code might really be doing:
foo.set_x(foo.get_x() + 1)
The first statement is infinitely more readable. Yet, with properties, you can add (at the beginning, or later on) the access control you get with the second approach.
Note, too, that instance variables starting with a single underscore are conventionally private. That is, the underscore signals to other developers that you consider the value to be private, and they shouldn't mess with it directly; however, nothing in the language prevents them from messing with it directly.
If you use a double leading underscore (e.g., __x
), Python does a little obfuscation of the name. The variable is still accessible from outside the class, via its obfuscated name, however. It's not truly private. It's just kind of ... more opaque. And there are valid arguments against using the double underscore; for one thing, it can make debugging more difficult.
Python doesn't have public OR private attributes. All attributes are accessible to all code.
self.attr = 0 #Done
Your method isn't in any way making _attr private, it's just a bit of obfuscation.
Quite simply, the OOP principles are wrong. Why this is is a long discussion which leads to flamewars and is probably off topic for this site. :-)
In Python there is not private attributes, you can't protect them, and this is never a real problem. So don't. Easy! :)
Then comes the question: Should you have a leading underscore or not. And in the example you have here you should definitely not. A leading underscore in Python is a convention to show that something is internal, and not a part of the API, and that you should use it on your own risk. This is obviously not the case here, but it's a common and useful convention.
The "dunder" (double underscore, __
) prefix prevents access to attribute, except through accessors.
class Foo():
def __init__(self):
self.__attr = 0
@property
def attr(self):
return self.__attr
@attr.setter
def attr(self, value):
self.__attr = value
@attr.deleter
def attr(self):
del self.__attr
Some examples:
>>> f = Foo()
>>> f.__attr # Not directly accessible.
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> '__attr' in f.__dir__() # Not listed by __dir__()
False
>>> f.__getattribute__('__attr') # Not listed by __getattribute__()
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__attr'
>>> f.attr # Accessible by implemented getter.
0
>>> f.attr = 'Presto' # Can be set by implemented setter.
>>> f.attr
'Presto'
>>> f.__attr = 'Tricky?' # Can we set it explicitly?
>>> f.attr # No. By doing that we have created a
'Presto' # new but unrelated attribute, same name.
However, you can access this type of attribute through name mangling (_classname__attribute
), which Python does in the background:
>>> f._Foo__attr
0
>>> f.__getattribute__('_Foo__attr')
0