Reputation: 5597
I am unsure how to phrase the question I am asking so feel free to change the title.
Currently, I am working on an existing python codebase and came across this "style" and hope to understand the benefits for using it.
for example,
Class Pokemon(object):
def __init__(self, name):
self.name = name
def _catch(self, pokeball):
''' actual implementation here'''
def catch(self, pokeball):
_catch(pokeball)
You may notice that calls to the catch() function will be rerouted to the _catch() function. I understand that an underscore before a function could be meant to prevent accidental overriding of the function.
EDIT: I think the title should be modified again, I understand what the underscores mean however I was unsure of why we use a catch() and _catch() as we obviously want to expose the function with catch() but decide to stick the implementation within _catch().
Upvotes: 0
Views: 1414
Reputation: 366003
Usually, this kind of design is used in two related (but nearly-opposite) patterns, which I don't know the "design patterns" names for. (I think they both include "engine", and one includes "template", if that helps.)
For the first, the idea is to allow a subclass to override the public catch
method to, say, add a bit of extra work before or after the core implementation, but still call the _catch
method to do most of the work. For example:
Class Pokemon(object):
def __init__(self, name):
self.name = name
def _catch(self, pokeball):
''' actual implementation here'''
# hundreds of lines of complex code
print(pokeball)
return pokeball
def catch(self, pokeball):
print('Gotta catch em all')
return self._catch(pokeball)
class Pikachu(Pokemon):
def catch(self, pokeball):
print('Pikachu')
return self._catch(pokeball)
This allows Pikachu
to override the "non-core" part of the implementation, which is only a few lines, without overriding the "core" part, which is hundreds of lines.
This pattern isn't nearly as common in Python as it is in, say, Java, but it does sometimes make sense.
For the other, the idea is to have the base class break the implementation up into separate pieces, each of which can be overridden by the subclass without having to replace everything else. For example:
class Pokemen(object):
def catch(self, pokeball):
self._wake_up()
if not self._check_ready() return False
try:
return self._catch(pokeball)
except SillyError:
return False
finally:
self.consider_sleeping()
So, why use a leading underscore?
The leading single underscore means "private by convention". For a method name, in particular,* it's a hint to the human reader that something is not part of the API. Anyone who wants to use a Pokemon
object should not call _catch
on it, because that method is an implementation detail—it may change or even go away in future versions, it may make assumptions about the object's state that aren't guaranteed to always be true, etc. But catch
should always be safe to call.
Often this is a good match for something that you'd make a protected
method in Java or C++, which is exactly what you'd use for both of these design patterns in those languages, even though it doesn't really mean the same thing.
A leading double underscore (without a trailing double underscore) means something different. In a method name or other attribute, it means the name should be "mangled" so that it's harder for a subclass or superclass to accidentally call it, or override it, when it intended to define and use its own private name instead.
Often, this is a good match for something that you'd make a private
method or member in Java or C++, but it's even farther from that than a single underscore is from protected
.
* In a few other places, it actually does have a tiny bit more meaning. For example, a module global with a leading underscore will be skipped by from mod import *
if you didn't specify an __all__
in mod
.
Upvotes: 4