Python名

2024-05-20 00:05:25 发布

您现在位置:Python中文网/ 问答频道 /正文

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

对Python来说也是这样吗?我应该先在所有东西上使用两个前导下划线,然后根据需要减少它们的隐藏(只有一个下划线)?

如果公约只使用一个下划线,我还想知道理由。

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

I come from languages that train you to think everything should be only as public as needed and no more. The reasoning is that this will reduce dependencies and make the code safer to alter. The Python way of doing things in reverse -- starting from public and going towards hidden -- is odd to me.


Tags: andtheto代码from语言内容that
3条回答

首先-什么是名字芒林?

当您在类定义中并使用__any_name__any_name_时,将调用名称管理,即,两个(或更多)前导下划线和最多一个尾随下划线。

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

现在:

>>> [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_'

When in doubt, do what?

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

一个潜在的价值在于避免与希望重写行为的子类发生名称冲突,从而使父类功能保持正常工作。然而,Python文档中的example不是Liskov可替换的,我在哪里发现这一点很有用呢。

缺点是,它增加了读取和理解代码基的认知负载,尤其是在调试时,当您在源代码中看到双下划线名称,在调试器中看到损坏的名称时。

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

你确实需要意识到这一点,这样当你看到它的时候就知道了。

政治公众人物8

《Python标准库样式指南》(Python standard library style guide)目前说(节略):

There is some controversy about the use of __names.

If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores.

  1. Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.

  2. Name mangling can make certain uses, such as debugging and __getattr__() , less convenient. However the name mangling algorithm is well documented and easy to perform manually.

  3. Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers.

它是如何工作的?

如果在类定义中预加两个下划线(不以双下划线结尾),则名称将被损坏,并且在对象上预加一个下划线,后跟类名:

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

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

>>> 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'

另外,那些Python新手有时很难理解当他们不能手动访问在类定义中定义的名称时会发生什么。这并不是反对它的有力理由,但如果你有一个学习型的听众,这是需要考虑的。

一个下划线?

If the convention is to use only one underscore, I'd also like to know the rationale.

当我的意图是让用户远离某个属性时,我倾向于只使用一个下划线,但这是因为在我的心理模型中,子类可以访问该名称(它们总是可以访问该名称,因为它们可以很容易地发现损坏的名称)。

如果我在查看使用__前缀的代码,我会问他们为什么要调用名称管理,如果他们不能使用一个下划线,请记住,如果子类为类和类属性选择相同的名称,那么尽管如此,名称冲突还是存在的。

我不认为实践会产生更好的代码。可见性修改器只会分散您对手头任务的注意力,并作为一种副作用强制您的界面按预期使用。一般来说,如果程序员没有正确阅读文档,强制可见性可以防止他们把事情弄糟。

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

我在Python中的策略是:

  1. 就写这该死的东西,不要假设你的数据应该如何保护。这假设您编写代码是为了为您的问题创建理想的接口。
  2. 如果可能不会在外部使用,并且不是普通“客户机代码”接口的一部分,请使用前导下划线。
  3. 只对类中纯粹方便使用的内容使用双下划线,否则如果意外暴露会造成相当大的损坏。

最重要的是,一切都应该清楚。如果其他人要使用它,请记录下来。如果你想在一年后有用的话,把它记录下来。

顺便说一句,您应该在其他语言中使用protected:您永远不知道您的类以后可能会被继承,也不知道它的用途。最好只保护那些您确定不能或不应该被外部代码使用的变量。

如果有疑问,请将其保留为“public”-我的意思是,不要添加任何内容来模糊属性的名称。如果你有一个具有内部值的类,不要为它操心。而不是写作:

class Stack(object):

    def __init__(self):
        self.__storage = [] # Too uptight

    def push(self, value):
        self.__storage.append(value)

默认情况下写入:

class Stack(object):

    def __init__(self):
        self.storage = [] # No mangling

    def push(self, value):
        self.storage.append(value)

这无疑是一种有争议的做法。Python新手只是讨厌它,甚至有些老Python家伙也鄙视这个默认值——但无论如何它都是默认值,所以我真的建议您遵循它,即使您觉得不舒服。

如果您真的想发送消息“Can't touch this!”对于用户,通常的方法是在变量前面加上一个下划线。这只是一个惯例,但人们理解它,在处理这些事情时要加倍小心:

class Stack(object):

    def __init__(self):
        self._storage = [] # This is ok but pythonistas use it to be relaxed about it

    def push(self, value):
        self._storage.append(value)

这对于避免属性名和属性名之间的冲突也很有用:

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

那双下划线呢?好吧,双下划线魔法主要用于to avoid accidental overloading of methods and name conflicts with superclasses' attributes。如果您编写一个预期会被多次扩展的类,那么它会非常有用。

如果你想把它用于其他用途,你可以,但它既不常见也不推荐。

编辑:为什么会这样?好吧,通常的Python风格并不强调让事情私有化——恰恰相反!这有很多原因-其中大多数都有争议。。。让我们看看其中的一些。

Python具有属性

现在大多数OO语言都使用相反的方法:不应该使用的东西不应该是可见的,所以属性应该是私有的。理论上,这将产生更易于管理、耦合更少的类,因为没有人会轻率地更改对象内部的值。

然而,这并不是那么简单。例如,Java类确实有很多属性getter,它们只是获取setter,它们只是设置值。我们可以说,您需要七行代码来声明一个属性——Python程序员会说这是不必要的复杂。另外,在实践中,您只需编写这么多代码来获得一个公共字段,因为您可以使用getter和setter更改其值。

那么,为什么要遵循这种默认的私有策略呢?默认情况下,只需公开您的属性。当然,这在Java中是有问题的,因为如果您决定向属性添加一些验证,它将要求您更改所有

person.age = age;

在你的代码中,让我们说

person.setAge(age);

setAge()是:

public void setAge(int age) {
    if (age >= 0) {
        this.age = age;
    } else {
        this.age = 0;
    }
}

所以在Java(和其他语言)中,默认情况是无论如何都要使用getter和setter,因为它们编写起来很烦人,但是如果您发现自己处于我描述的情况下,则可以节省大量时间。

但是,不需要在Python中执行,因为Python具有属性。如果你有这门课:

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self.age = age

然后决定验证ages,不需要更改代码的person.age = age部分。只需添加一个属性(如下所示)

 class Person(object):
     def __init__(self, name, age):
         self.name = name
         self._age = age if age >= 0 else 0

     @property
     def age(self):
         return self._age

     @age.setter
     def age(self, age):
         if age >= 0:
             self._age = age
         else:
             self._age  = 0

如果您可以这样做并且仍然使用person.age = age,为什么要添加私有字段、getter和setter?

(另请参见Python is not Javathis article about the harms of using getters and setters。)。

不管怎样,一切都是可见的,试图隐藏只会使你的工作复杂化

即使在存在私有属性的语言中,也可以通过某种反射/内省库访问它们。人们做了很多,在框架和解决紧急需求。问题是,内省库只是一种很难处理公共属性的方法。

由于Python是一种非常动态的语言,因此将此负担添加到类中只会适得其反。

问题是看不到-它是必需才能看到的

对于Pythonista来说,封装并不是不能看到类的内部,而是可以避免看到它。我的意思是,封装是组件的属性它允许用户在不关心内部细节的情况下使用它。如果您可以使用一个组件而不必担心它的实现,那么它是封装的(在Python程序员看来)。

现在,如果您以这样一种方式编写您的类,您可以使用它而不必考虑实现细节,那么如果您想让以某种原因查看类内部,就没有问题。关键是:你的API应该很好,剩下的就是细节。

圭多是这么说的

好吧,这没有争议:he said so, actually。(寻找“开放和服”。)

这就是文化

是的,有一些原因,但没有关键的原因。这主要是Python编程的一个文化方面。坦率地说,这也可能是另一种方式,但事实并非如此。另外,您也可以很容易地问另一个问题:为什么有些语言在默认情况下使用私有属性?与Python实践的主要原因相同:因为它是这些语言的文化,并且每种选择都有其优缺点。

既然已经有了这种文化,你最好遵循它。否则,当您在堆栈溢出中询问问题时,Python程序员告诉您从代码中删除__会让您恼火:)

相关问题 更多 >