MaxB MaxB - 4 months ago 12
Python Question

How to avoid accidentally messing up the base class in Python?

Python uses the underscore convention for private variables. However, nothing seems to stop you from messing up the base class accidentally in, for example

class Derived(Base):
def __init__(self, ...):
...
super(Derived, self).__init__(...)
...
self._x = ...


if
Base
also happens to use the name
_x
.

What are the best practices to avoiding this kind of mistake?

This seems especially challenging, if different people implemented
Base
and
Derived
classes, or
_x
was added to
Base
after
Derived
was implemented (So, the implementation of
Derived
would be breaking the encapsulation retroactively)

Answer

Use private variables with two underscores. This way, name mangling protects you from messing up your parent class (in normal use cases).

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.

[...] Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger.

Example

class A(object):
    def __init__(self):
        self.__v = 1
    def __str__(self):
        return "A = {}".format(self.__v)

class B(A):
    def __init__(self):
        A.__init__(self)
        self.__v = 2
    def __str__(self):
        return "{}; B = {}".format(A.__str__(self), self.__v)

a = A()
b = B()
print(a)
print(b)

yields

A = 1
A = 1; B = 2