Python 函数前带有 single/double 下划线的命名约定是什么意思?

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

我不确定如何表达我要问的问题,所以请随时更改标题。

目前,我正在处理现有的 python 代码库并遇到了这个 "style" 并希望了解使用它的好处。

例如,

Class Pokemon(object):
   def __init__(self, name):
        self.name = name

   def _catch(self, pokeball):
       ''' actual implementation here'''

   def catch(self, pokeball):
       _catch(pokeball)

您可能会注意到,对 catch() 函数的调用将被重新路由到 _catch() 函数。我知道函数前的下划线可能是为了防止意外覆盖函数。

编辑:我认为标题应该再次修改,我理解下划线的意思但是我不确定为什么我们使用 catch() 和 _catch() 因为我们显然想用 catch() 公开函数但决定将实现坚持在 _catch() 中。

通常,这种设计用于两个相关(但几乎相反)的模式,我不知道 "design patterns" 名称。 (我认为它们都包含 "engine",其中一个包含 "template",如果有帮助的话。)


首先,想法是允许子class 覆盖public catch 方法,例如,在核心之前或之后添加一些额外的工作实现,但仍然调用 _catch 方法来完成大部分工作。例如:

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)

这允许 Pikachu 覆盖实现的 "non-core" 部分,它只有几行,而不覆盖 "core" 部分,它有数百行。

这种模式在 Python 中并不像在 Java 中那样常见,但它有时确实有意义。


另一方面,想法是让基础 class 将实现分解成单独的部分,每个部分都可以被子 class 覆盖,而无需替换其他所有部分。例如:

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

那么,为什么要使用前导下划线?

前导单下划线表示"private by convention"。对于方法名称,特别是,* 它是一个提示 给人类 reader 某些东西不属于 API.任何想要使用 Pokemon 对象的人都不应该对其调用 _catch,因为该方法是一个实现细节——它可能会在未来的版本中改变甚至消失,它可能会对对象的状态做出假设不能保证总是正确的,等等。但是 catch 应该总是可以安全调用。

通常这与您在 Java 或 C++ 中创建 protected 方法的东西非常匹配,这正是您在这些设计模式中使用的东西语言,即使它实际上并不意味着同一件事。


前导双下划线(没有尾随双下划线)意味着不同的东西。在方法名称或其他属性中,这意味着名称应该是 "mangled",这样 subclass 或 superclass 就更难意外地调用它,或者在需要时覆盖它改为定义和使用自己的私有名称。

通常,这非常适合您在 Java 或 C++ 中创建 private 方法或成员的内容,但它与 protected.


* 在其他一些地方,它确实有更多的意义。例如,如果您未在 mod.

中指定 __all__,则 from mod import * 将跳过具有前导下划线的全局模块