-1

It seems to me that there's an inherent difficulty in Python when a programmer has to work with a class developed by someone else about which he/she has essential documentation (how to construct it, what functions are available etc) but not details of its implementation. Of course one could examine the source code when things start to go wrong but said class may be entirely robust until someone wants to subclass it.

Consider this trivial example:-

class Base():
    def __init__(self):
        self._n = 99

    @property
    def n(self):
        return self._n

    @n.setter
    def n(self, n):
        self._n = n

    def func(self):
        print(self.n)

The key thing here is that Base has a function that relies, in some way, on the attribute _n.

I now decide that I want to develop a class that sub-classes Base. So, I write this:-

class Myclass(Base):
    def __init__(self, n):
        super().__init__()
        self.n = n

Now I'm in trouble because I've just zapped an attribute in my superclass and, potentially, completely broken its functionality.

Are there any development patterns or any other forms of defence against this potential problem?

  • Looks fine to me. If the superclass documentation doesn't make the relationship between `func` and `n` clear, that means the documentation is inadequate. The solution is better documentation. – user2357112 Aug 25 '21 at 16:43
  • 1
    What do you mean by "zapped an attribute"? You're still using the property/setter in the init – Iain Shelvington Aug 25 '21 at 16:44
  • 2
    Bigger problems occur when a superclass and a subclass both try to use an `_n` attribute for unrelated things and assume they don't need to document it because it's "private". – user2357112 Aug 25 '21 at 16:45
  • 1
    Can you clarify your issue? The code as shown seems perfectly fine. – MisterMiyagi Aug 25 '21 at 16:46
  • 1
    Note that since inheritance is inherently about extending an *implementation*, there is no way around having to know the implementation. If one does not want to rely on implementation details or a class is not meant for subclassing, then inheritance is just not the right tool – other patterns such as composition, protocols / structural types, etc. *may* apply, but there is no general replacement. – MisterMiyagi Aug 25 '21 at 16:50
  • It's hard to understand what you think is wrong here. Base explicitly exposes a public attribute, and it is being used. If you don't want it to be used, don't expose it as part of the public API. As an aside, there is no point to the `property` here and it shouldn't be used – juanpa.arrivillaga Aug 25 '21 at 17:04
  • Although not strictly *private*, judicious use of a dunder prefix solves this particular problem. Thanks for the comments –  Aug 25 '21 at 18:34

1 Answers1

1

You can avoid "zapping" the superclass's attributes by using name mangling for "private"* attributes. Attributes whose names begin with two underscores** will have their names automatically mangled in such a way that they won't collide with names declared in the superclass.

class A:
    def __init__(self):
        self.__x = 1
    def get_A_x(self):
        return self.__x

class B(A):
    def __init__(self):
        super().__init__()
        self.__x = 2
    def get_B_x(self):
        return self.__x

obj = B()
print(obj.get_A_x(), obj.get_B_x())
# prints "1 2"

In the best case, the superclass would always be written to use name-mangling in order to protect its own "private" attributes. If not, then at least you can be sure that name-mangled attributes in the subclass won't collide with them; there would still be a risk that public attributes of the subclass could collide with unknown "private" attributes of the superclass. In that case the best you can do (other than open an issue on their issue tracker) would be try to find out what undocumented attributes the superclass declares by inspecting the __dict__ attribute of a superclass instance, and then avoiding collisions with those names.

*Of course in Python, no attributes are truly private. Even name-mangled attributes can be accessed from outside the class which declares them, if they are accessed via their mangled names.
**Not including "dunder methods" like __init__, or any other names which also end with two or more underscores.

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • 1
    "except for the special "dunder methods" like ``__init__``" – there are no reserved dunder names. The rule for mangling is ["at least two leading underscores, at most one trailing underscore"](https://docs.python.org/3/tutorial/classes.html#private-variables) and every name not matching this is used verbatim. – MisterMiyagi Aug 25 '21 at 17:42
  • @MisterMiyagi Yes, that was poorly phrased, I've edited to fix. Thank you. – kaya3 Aug 25 '21 at 17:49