简体   繁体   中英

What does the Python naming convention with single/double underscore before a function mean?

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().

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM