在函数之前使用单/双下划线的Python命名约定是什么意思?

时间:2015-05-23 00:36:04

标签: python naming-conventions name-mangling

我不确定如何说出我要问的问题,所以随时可以更改标题。

目前,我正在开发一个现有的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()中。

1 个答案:

答案 0 :(得分:3)

通常,这种设计用于两种相关(但几乎相反)的模式,我不知道“设计模式”的名称。 (我认为它们都包含“引擎”,如果有帮助,则包含“模板”。)

首先,我们的想法是允许子类覆盖公共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覆盖实现的“非核心”部分,这只是几行,而不会覆盖“核心”部分,即数百行。

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

另一方面,我们的想法是让基类将实现分解为单独的部分,每个部分都可以被子类覆盖,而不必替换其他所有部分。例如:

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

那么,为什么要使用领先的下划线?

领先的单一下划线意味着“按惯例私有”。对于方法名称,特别是 * ,它是人类读者的提示某些东西不是API的一部分。任何想要使用Pokemon对象的人都不应该在其上调用_catch,因为该方法是一个实现细节 - 它可能会在未来的版本中发生变化甚至消失,它可能会对对象的状态做出假设不保证总是如此,等等。但是catch应始终可以安全地打电话。

这通常适合您在Java或C ++中使用protected方法的东西,这正是您在这些语言中使用这两种设计模式的原因,即使它没有这真的意味着同样的事情。

领先的双下划线(没有尾随的双下划线)意味着不同的东西。在方法名称或其他属性中,这意味着名称应该被“修改”,以便当子类或超类意图定义和使用自己的私有名称时,它更难以意外地调用它或覆盖它。

通常情况下,这与您在Java或C ++中创建private方法或成员的内容非常匹配,但它比protected中的单个下划线更远。 / p>

*在其他一些地方,它实际上确实有更多的含义。例如,如果您未在from mod import *中指定__all__,则会mod跳过带有前导下划线的模块全局。