Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"public" or "private" attribute in Python ? What is the best way?

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 ?

like image 553
Sandro Munda Avatar asked Dec 29 '10 16:12

Sandro Munda


People also ask

Should attributes be public or private?

You are better off in the long term if all attributes are private. That includes not using getters. If you are accessing attributes of objects then you are breaking encapsulation, and aren't really doing OOP at all. At that point, most of the reason for building a class has been wasted.

Should I use private methods in Python?

Private methods are useful for breaking tasks up into smaller parts, or for preventing duplication of code which is needed often by other methods in a class, but should not be called outside of that class.

How do you use private attributes in Python?

In Python, there is no existence of Private methods that cannot be accessed except inside a class. However, to define a private method prefix the member name with double underscore “__”. Note: The __init__ method is a constructor and runs as soon as an object of a class is instantiated.

Does Python have public and private?

There are three types of access modifiers in Python: public, private, and protected. Variables with the public access modifiers can be accessed anywhere inside or outside the class, the private variables can only be accessed inside the class, while protected variables can be accessed within the same package.


2 Answers

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.

like image 86
Brian Clapper Avatar answered Oct 05 '22 17:10

Brian Clapper


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 
like image 30
brannerchinese Avatar answered Oct 05 '22 16:10

brannerchinese