Reputation: 93
When creating a subclass in python, how do I ensure that my subclass implementation methods don't unintentionally override an implementation method in a superclass? I'm trying to avoid:
class A:
def a(self):
self._helper_func()
def _helper_func(self):
# do something specific to A
class B(A):
def b(self):
self._helper_func()
def _helper_func(self):
# do something; didn't realize this would mess up a()
I'm thinking the only options are: (1) read the source code of the super class to ensure I'm picking a unique subclass method name (which may break when the superclass is updated); or (2) use __ on all implementation methods in my subclass (usually __ is described as a way for a superclass to prevent subclassing; but I'm thinking it would help a subclass avoid breaking the superclass).
Am I missing something here?
Update @sobolevn basically answered my question. To clarify: all of the discussion I've seen on name-mangling centers around superclasses trying to hide/prevent their methods from being called/overridden (and there's usually comment on how this isn't the python way and users should be respectful of a single leading _). I'm concerned about the very different problem of subclassing a class that I didn't write (and which did not, itself, use name mangling), and choosing names for implementation methods, which could inadvertently break code in the superclass (or its parents) if that name was already used.
Based on what I've read, it seems to me that (a) best practice for writing a subclass of class(es) that I didn't write myself would be to prefix __ to all private methods; but (b) given that all methods are virtual in python and I often will want my subclass to expose some new public/protected method, there is no good alternative to being fully aware of all the non-mangled method names used by all the classes I inherit from, and this awareness would obviate the requirement to worry about (a).
Upvotes: 4
Views: 2328
Reputation: 18080
PEP8 says:
Use one leading underscore only for non-public methods and instance variables.
To avoid name clashes with subclasses, use two leading underscores to invoke Python's name mangling rules.
Python mangles these names with the class name: if class Foo has an attribute named __a, it cannot be accessed by Foo.__a. (An insistent user could still gain access by calling Foo._Foo__a.) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed.
What are the differences between them? Look at this example:
class Parent(object):
""" This parent has all types of methods. """
def _will_private_run_test(self):
self.__private()
def public(self):
print('public', self.__class__)
def _protected(self):
print('protected', self.__class__)
def __private(self):
print('private', self.__class__)
class Child(Parent):
""" This child only knows parent's methods. """
pass
class ChildWithOverrides(Parent):
""" This child overrides all. """
def public(self):
print('New', 'public', self.__class__)
def _protected(self):
print('New', 'protected', self.__class__)
def __private(self):
print('New', 'private', self.__class__)
def run_private(obj):
if hasattr(obj, '__private'):
print('A mirracle happened!')
try:
obj.__private()
except AttributeError as ae:
print(ae)
parent = Parent()
parent._will_private_run_test()
child = Child()
teen = ChildWithOverrides()
parent.public()
child.public()
teen.public()
parent._protected()
child._protected()
teen._protected()
run_private(parent)
run_private(child)
run_private(teen)
The output will be:
('private', <class '__main__.Parent'>)
('public', <class '__main__.Parent'>)
('public', <class '__main__.Child'>)
('New', 'public', <class '__main__.ChildWithOverrides'>)
('protected', <class '__main__.Parent'>)
('protected', <class '__main__.Child'>)
('New', 'protected', <class '__main__.ChildWithOverrides'>)
'Parent' object has no attribute '__private'
'Child' object has no attribute '__private'
'ChildWithOverrides' object has no attribute '__private'
As you can see it is impossible to call __private method from the outside.
Upvotes: 7