76

In Python, I have the following example class :

class Foo:
    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

As you can see, I have a simple "private" attribute "_attr" and a property to access it. There is a lot of codes to declare a simple private attribute and I think that it's not respecting the "KISS" philosophy to declare all attributes like that.

So, why not declare all my attributes as public attributes if I don't need a particular getter/setter/deleter ?

My answer will be : Because the principle of encapsulation (OOP) says otherwise!

What is the best way ?

Billal Begueradj
  • 20,717
  • 43
  • 112
  • 130
Sandro Munda
  • 39,921
  • 24
  • 98
  • 123
  • What do you mean by "declare"? Python has no declarations. So it's unclear what you mean when you use the word "declare". Please update your question to either replace the word "declare" or define what you mean by it. – S.Lott Dec 29 '10 at 20:37
  • 2
    @SLott That's strange because e.g. [chapter 9 of the Python tutorial](https://docs.python.org/3/tutorial/classes.html) mentions declarations (e.g. in the context of scoping). It seems to be used in the common sense (which could maybe be stated as "a statement, directive or expression that introduces a name"). – Arne Vogel Jul 29 '19 at 11:24
  • Related: https://stackoverflow.com/questions/1641219/does-python-have-private-variables-in-classes. – AMC May 29 '20 at 04:08

9 Answers9

111

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, not foo.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 invokes foo.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.

Brian Clapper
  • 25,705
  • 7
  • 65
  • 65
  • 3
    How do I argue/discuss with a Java dev that this does not make the programming language "worse" or that it doesnt just "miss a feature"? – raitisd Dec 15 '16 at 15:18
  • 6
    I always point out that, unlike Ruby, Python, Scala, and C# (to name just a few), Java is actually worse, because it demonstrably fails to adhere to the Uniform Access Principle. As a result, the coder has to type (or the IDE has to generate) getters and setters defensively, most of which don't add any real value—and all of which mean there's more useless code to wade through when maintaining the class. – Brian Clapper Feb 04 '17 at 20:27
  • @BillalBEGUERADJ : You can, like this: `_ClassName__x`. [See this question.](https://stackoverflow.com/q/70528/9445557) – Artemis Jun 02 '18 at 13:44
  • 5
    @BrianClapper "fails to adhere to the Uniform Access Principle" - coming from the JVM world, this is what we're thought to avoid as it makes the code less "secure". I mean, let's imagine we have some mutable properties internal to the class and it's business logic that you want to hide from others... not sure I'd want that public. I'm interested in finding out why you think it is important to have this principle adhered to when building a language. Totally open-minded, give me some refs – milosmns Jul 26 '19 at 12:51
  • 2
    @milosmns If someone has access to the code base, you can't hide the business logic from them. See [this](https://en.wikipedia.org/wiki/Uniform_access_principle#Explanation) and [this](https://martinfowler.com/bliki/UniformAccessPrinciple.html) for why UAP might be better, apart from the answer itself. – Harshvardhan May 28 '22 at 14:45
41

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
Philippe Fanaro
  • 6,148
  • 6
  • 38
  • 76
brannerchinese
  • 1,909
  • 5
  • 24
  • 40
  • 6
    However... [you *can* access the attribute through a special way](https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references): `f.__getattribute__('_Foo__attr')`. – Philippe Fanaro Jan 13 '20 at 19:30
  • 1
    ^^ For Python 3 https://docs.python.org/3/tutorial/classes.html#private-variables – aydow Oct 25 '22 at 06:57
21

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.

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
  • 3
    "_In Python there is not private attributes_". So many times have I had to explain that encapsulation does not work similarly to other programming languages like Java... – renatodamas Jul 27 '20 at 14:19
6

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.

Tyler Eaves
  • 12,879
  • 1
  • 32
  • 39
  • 3
    I know, but it's not my question. As you can see in my post, "private" and "public" are arrounded by quotes. – Sandro Munda Dec 29 '10 at 16:47
  • 1
    Your question is meaningless then. It's like asking how to do garbage collection in C without using external libs. The facility just isn't built into the language. – Tyler Eaves Dec 29 '10 at 16:52
  • @SeyZ if you want private and public and all that jazz, you should try Java, C# etc., Python isn't going to get it done for you – David Heffernan Dec 29 '10 at 16:55
  • 5
    Nevertheless, the use of a leading underscore (or two, depending) is a well-established Python convention meaning, roughly, "Hey, don't access this directly. I might change it, remove it, or otherwise mess with it in future revisions of this class." Granted, that's not enforced privacy; it *is*, however, widely used in Python, and it is understood as being a form of *conventional* privacy. – Brian Clapper Dec 29 '10 at 17:00
5

See this link:https://docs.python.org/2/tutorial/classes.html

9.6. Private Variables and Class-local References

“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.

Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.

Community
  • 1
  • 1
W.Perrin
  • 4,217
  • 32
  • 31
4

As others have said, private attributes in Python are merely a convention. The use of property syntax should be used for special processing when attributes are bound, modified or deleted. The beauty of Python is that you can start off by just using normal attribute binding, e.g., self.attr = 0 and if at some later date you decide you want to restrict the value of attr to say 0 <= attr <=100, you can make attr a property and define a method to make sure this condition is true without ever having to change any user code.

Don O'Donnell
  • 4,538
  • 3
  • 26
  • 27
2

To make an attribute private, you just have to do self.__attr

class Foo:
    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
Will
  • 1,124
  • 12
  • 33
  • 7
    It should be noted that this doesn't actually make the variable private from the encapsulation perspective. All this does it perform a name mangling operation which CHANGES the way that one access the variable, but does not have the same effect as a true private variable from a OOP language such as Java. One need only access the variable at the name mangled name to get access to it. – AfroRick Sep 07 '17 at 12:33
0

In Python, unless you need special behavior out of an attribute, there's no need to hide it behind accessor methods. If an attribute is for internal use only, prepend it with an underscore.

Chris
  • 3,438
  • 5
  • 25
  • 27
0

The nice thing about properties is that they given you a really cool interface to work with. Sometimes it's handy to derive a property based on some other (ie. BMI is defined by weight and height). The user of the interface doesn't have to know this of course.

I prefer this way over having explicit getters and setters like in Java ie. Way nicer. :)

Juho Vepsäläinen
  • 26,573
  • 12
  • 79
  • 105