一尘不染

Python名称修饰

python

在其他语言中,有助于产生更好代码的通用准则总是使所有内容都尽可能隐藏。如果不确定变量是私有变量还是受保护变量,最好使用私有变量。

同样适用于Python吗?我是否应该一开始在所有内容上使用两个前导下划线,并且仅在需要时才使它们隐藏程度降低(仅一个下划线)?

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

这是我对JBernardo的回答所留下的评论。它解释了为什么我问这个问题,以及为什么我想知道为什么Python与其他语言不同的原因:


阅读 513

收藏
2020-02-16

共1个答案

一尘不染

如有疑问,请将其保留为“ 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人士都鄙视了此默认设置-但无论如何它都是默认设置,因此即使你感到不舒服,我也建议你遵循它。

如果你确实要发送消息“无法触摸此!” 对于你的用户,通常的方法是在变量前加一个下划线。这只是一个约定,但人们理解它并在处理此类内容时要格外小心:

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

那双下划线呢?好吧,双下划线魔术主要用于避免方法的意外重载以及与超类的属性发生名称冲突。如果你编写一个预期会扩展很多次的类,这将非常有用。

如果你想将其用于其他目的,可以,但是既不推荐也不推荐使用。

编辑:为什么会这样?好吧,通常的Python风格并不强调将事情变成私有的-相反!造成这种情况的原因很多-其中大多数存在争议…让我们看看其中的一些原因。

Python具有属性

如今,大多数OO语言都使用相反的方法:不应该使用的内容应该不可见,因此属性应该是私有的。从理论上讲,这将产生更易于管理,耦合更少的类,因为没有人会不顾一切地更改对象内部的值。

但是,它并不是那么简单。例如,Java类确实有很多的属性和 getter方法,只是得到的值和刚刚制定者设定的值。让我们说,你需要七行代码来声明一个属性-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

然后你决定验证年龄,则无需更改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不是Java,而本文则介绍了使用getter和setter的危害。)

一切都是可见的-试图隐藏只会使你的工作复杂化
即使在具有私有属性的语言中,你也可以通过某种反射/自省库来访问它们。人们在框架和解决紧急需求方面做了大量工作。问题在于,自省库只是完成公共属性所用的一种困难方法。

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

无法看到问题- 必须看到
对于Pythonista,封装不是无法看到类的内部,而是避免查看它的可能性。我的意思是说,封装是组件的属性,它使组件可以使用而无需用户担心内部细节。如果你可以使用某个组件而不必担心其实现,那么它将被封装(Python程序员认为)。

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

圭多这么说

好吧,这没有争议:实际上他是这么说的。(寻找“开放和服”。)

这是文化

是的,有一些原因,但没有关键原因。这主要是Python编程的文化方面。坦白说,也可能是另一种方式,但事实并非如此。同样,你可以轻松地反过来问:为什么某些语言默认使用私有属性?出于与Python实践相同的主要原因:因为这是这些语言的文化,每种选择都有其优点和缺点。

由于已经存在这种文化,因此建议你遵循它。否则,__当你在Stack Overflow中提问时,Python程序员会告诉你将其从代码中删除,这会使你感到烦恼:)

2020-02-16