Python名称修改

时间:2011-09-17 18:07:50

标签: python naming-conventions

在其他语言中,有助于生成更好代码的一般准则总是尽可能隐藏所有内容。如果对变量是私有还是受保护存在疑问,最好与私有变量一起使用。

Python也适用吗?我是否应该首先在所有内容上使用两个前导下划线,并且只在我需要时将它们隐藏起来(只有一个下划线)?

如果惯例只使用一个下划线,我也想知道其基本原理。

以下是我留在JBernardo's answer的评论。它解释了为什么我问这个问题以及为什么我想知道为什么Python与其他语言不同:

  

我来自语言,训练你认为一切都应该只是公开的,而不是更多。原因是这将减少依赖性并使代码更安全。反向做事的Python方式 - 从公共开始到隐藏 - 对我来说很奇怪。

10 个答案:

答案 0 :(得分:153)

答案 1 :(得分:14)

我不会说练习会产生更好的代码。可见性修饰符只会分散您对手头任务的注意力,并且作为副作用会强制您的界面按预期使用。一般来说,强制执行可见性可以防止程序员在没有正确阅读文档的情况下搞乱。

更好的解决方案是Python鼓励的路径:您的类和变量应该有很好的文档记录,并且它们的行为是明确的。来源应该可用。这是编写代码的更具可扩展性和可靠性的方法。

我在Python中的策略是:

  1. 只要写下该死的东西,不要假设你的数据应该如何受到保护。这假定您编写代码来为您的问题创建理想的接口。
  2. 使用前导下划线表示可能不会在外部使用的内容,并且不属于正常的“客户端代码”界面。
  3. 仅在课堂内使用双重下划线,或者在不小心暴露时会造成相当大的伤害。
  4. 最重要的是,应该清楚所做的一切。如果其他人将使用它,请记录下来。如果您希望它在一年的时间内有用,请记录下来。

    作为旁注,您实际上应该使用其他语言中的 protected :您永远不会知道您的类可能会在以后继承以及它可能被使用。最好只保护那些您确定不能或不应该被外国代码使用的变量。

答案 2 :(得分:12)

首先 - 名称是什么?

当您在班级定义中并使用ALTER TABLE `data_update` ADD PRIMARY KEY(`id`); __any_name,即两个(或更多)前导下划线且最多一个尾随时,将调用名称修改下划线。

__any_name_

现在:

class Demo:
    __any_name = "__any_name"
    __any_other_name_ = "__any_other_name_"
  

如有疑问,可以做什么?

表面上的用法是防止子类使用该类使用的属性。

潜在的价值在于避免与想要覆盖行为的子类的名称冲突,以便父类功能保持按预期工作。但是,Python文档中的example不是Liskov可替代的,并且我没有想到我发现它有用的例子。

缺点是它会增加读取和理解代码库的认知负荷,尤其是在调试时,您会看到源中的双下划线名称和调试器中的错误名称。

我个人的做法是故意避免它。我在一个非常大的代码库上工作。它的罕见用途像拇指疼痛一样突出,似乎不合理。

你需要注意它,以便在你看到它时知道它。

PEP 8

PEP 8,Python标准库样式指南,目前说(删节):

  

关于>>> [n for n in dir(Demo) if 'any' in n] ['_Demo__any_name', '_Demo__any_other_name_'] >>> Demo._Demo__any_name '__any_name' >>> Demo._Demo__any_other_name_ '__any_other_name_' 的使用存在一些争议。

     

如果您的类要进行子类化,并且您具有不希望使用子类的属性,请考虑使用双前导下划线命名它们,并且不要使用尾随下划线。

     
      
  1. 请注意,在错位名称中只使用简单的类名,因此如果子类选择相同的类名和属性名,   你仍然可以得到名字冲突。

  2.   
  3. 名称修改可以使某些用途(例如调试和__names)不太方便。但是,名称修改算法已有详细记录,并且易于手动执行。

  4.   
  5. 不是每个人都喜欢名字错误。尽量平衡避免意外名称冲突与高级呼叫者可能使用的需要。

  6.   

它是如何工作的?

如果在类定义中添加两个下划线(不带双重下划线),则名称将被修改,并且下划线后跟类名称将被添加到对象上:

__getattr__()

请注意,只有在解析类定义时,名称才会被破坏:

>>> class Foo(object):
...     __foobar = None
...     _foobaz = None
...     __fooquux__ = None
... 
>>> [name for name in dir(Foo) if 'foo' in name]
['_Foo__foobar', '__fooquux__', '_foobaz']

此外,那些刚接触Python的人有时无法理解当他们无法手动访问他们在类定义中定义的名称时会发生什么。这并不是反对它的强有力理由,但如果您有学习观众,则需要考虑这一点。

One Underscore?

  

如果约定只使用一个下划线,我也想知道其基本原理。

当我的目的是让用户放弃属性时,我倾向于只使用一个下划线,但这是因为在我的心智模型中,子类可以访问名称(他们总是拥有,因为他们可以很容易发现被破坏的名字)。

如果我正在审查使用>>> Foo.__test = None >>> Foo.__test >>> Foo._Foo__test Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'Foo' has no attribute '_Foo__test' 前缀的代码,我会问他们为什么要调用名称修改,如果他们不能用单个下划线做同样的事情,请记住,如果是子类为类和类属性选择相同的名称,尽管如此,仍会有名称冲突。

答案 3 :(得分:7)

您不应该从私有数据开始,并在必要时将其公开。相反,您应该首先弄清楚对象的界面。即你应该首先弄清楚世界会看到什么(公共事物),然后找出必要的私人物品。

其他语言很难让那些曾经公开的私人语言。即如果我将变量设为私有或受保护,我会破坏大量代码。但是在python中的属性并非如此。相反,即使重新安排内部数据,我也可以保持相同的界面。

_和__之间的区别在于python实际上试图强制执行后者。当然,它并没有真正努力,但确实让它变得困难。 _只是告诉其他程序员意图是什么,他们可以自由地忽视他们的危险。但忽略这条规则有时会有所帮助。示例包括调试,临时黑客攻击以及使用不打算以您使用方式使用的第三方代码。

答案 4 :(得分:5)

已经有很多很好的答案,但我会提供另一个。这也部分是对那些一直说双下划线不是私密的(实际上是)的回应。

如果你看一下Java / C#,它们都有private / protected / public。所有这些都是编译时构造。它们仅在编译时强制执行。如果您在Java / C#中使用反射,则可以轻松访问私有方法。

现在,每次在Python中调用函数时,您都固有地使用反射。这些代码段在Python中是相同的。

lst = []
lst.append(1)
getattr(lst, 'append')(1)

“dot”语法只是后一段代码的语法糖。主要是因为使用getattr已经很难看,只有一个函数调用。它从那里变得更糟。

因此,不能是私有的Java / C#版本,因为Python不编译代码。 Java和C#无法在运行时检查某个函数是私有的还是公共的,因为该信息已经消失(并且它不知道函数的调用位置)。

现在有了这些信息,双下划线的名称变形对于实现“私密性”最有意义。现在当从'self'实例调用一个函数并且它注意到它以'__'开头时,它只是在那里执行名称修改。这只是更多的语法糖。这种语法糖允许使用仅使用反射进行数据成员访问的语言中的“私有”。

免责声明:我从未听过Python开发人员说过这样的话。缺乏“私人”的真正原因是文化,但你也会注意到大多数脚本/解释语言都没有私有。除编译时间外,严格可执行的私有内容不适用。

答案 5 :(得分:4)

第一:为什么要隐藏数据?为什么这么重要?

大部分时间你并不是真的想这么做,但是因为其他人正在这样做。

如果你真的真的不想让人们使用某些东西,请在它前面添加一个下划线。就是这样...... Pythonistas知道带有一个下划线的东西并不能保证每次都能正常工作,并且可能会在您不知情的情况下发生变化。

这就是我们生活的方式,我们对此感到满意。

使用两个下划线会使你的类很难进行子类化,即使你不想那样工作。

答案 6 :(得分:2)

乍一看它应该与其他语言相同(在“其他”我指的是Java或C ++),但事实并非如此。

在Java中,您将所有不应在外部访问的变量设为私有。在Python的同一时间,你无法实现这一点,因为没有“私有性”(正如Python原则所说 - “我们都是成年人”)。因此,双下划线仅表示“伙计们,不要直接使用此字段”。相同的含义有下划线,当你必须从被考虑的类继承时(同时也是由双下划线引起的可能问题的一个例子),同时不会引起任何麻烦。

因此,我建议您默认使用单个下划线作为“私人”成员。

答案 7 :(得分:2)

以下代码段将解释所有不同的情况:

  • 两个前导下划线(__a)
  • 单个前导下划线(_a)
  • 没有下划线(a)

    class Test:
    
    def __init__(self):
        self.__a = 'test1'
        self._a = 'test2'
        self.a = 'test3'
    
    def change_value(self,value):
        self.__a = value
        return self.__a
    

打印测试对象的所有有效属性

testObj1 = Test()
valid_attributes = dir(testObj1)
print valid_attributes

['_Test__a', '__doc__', '__init__', '__module__', '_a', 'a', 
'change_value']

在这里,您可以看到__a的名称已更改为_Test__a,以防止此变量被任何子类覆盖。这个概念在python中被称为“Name Mangling”。 您可以这样访问:

testObj2 = Test()
print testObj2._Test__a

test1

类似地,在_a的情况下,变量只是通知开发人员它应该被用作该类的内部变量,即使你访问它,python解释器也不会做任何事情,但它不是一个好的实践。

testObj3 = Test()
print testObj3._a

test2

变量可以从任何地方访问,就像公共类变量一样。

testObj4 = Test()
print testObj4.a

test3

希望答案能帮到你:)。

答案 8 :(得分:0)

“如果不确定变量是私有变量还是受保护变量,最好使用私有变量。” -是的,在Python中也是如此。

这里有一些回答是关于“惯例”的,但没有提供这些惯例的链接。 Python的权威指南PEP 8明确指出:

  

如有疑问,请选择非公开;稍后将其公开比将公共属性设为不公开要容易。

在其他答案中,还考虑了公共和私有之间的区别以及Python中的名称修改。在同一链接中,

  

我们在这里不使用术语“私有”,因为Python中没有任何属性是真正私有的(通常没有不必要的工作量)。

答案 9 :(得分:-3)

#用于Python名称修饰的示例程序

class Demo:
    __any_name = "__any_name"
    __any_other_name_ = "__any_other_name_"


[n for n in dir(Demo) if 'any' in n]   # GIVES OUTPUT AS ['_Demo__any_name', 
                                       #    '_Demo__any_other_name_']
相关问题