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 ?
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.
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.
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.
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.
Typically, Python code strives to adhere to the Uniform Access Principle. Specifically, the accepted approach is:
foo.x = 0
, not foo.set_x(0)
@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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With