关于类:python在类中有private变量吗?

Does Python have “private” variables in classes?

我来自Java世界,阅读Bruce Eckels的Python 3模式、食谱和习语。

在阅读关于类的内容时,它继续说在Python中不需要声明实例变量。你只需要在构造器中使用它们,Boom,它们就在那里。

例如:

1
2
3
4
5
6
7
8
9
10
class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

如果是这样,那么类Simple的任何对象都可以在类外更改变量s的值。

例如:

1
2
3
4
5
if __name__ =="__main__":
    x = Simple("constructor argument")
    x.s ="test15" # this changes the value
    x.show()
    x.showMsg("A message")

在Java中,我们已经学习了公共/私有/受保护变量。这些关键字是有意义的,因为有时您希望类中没有人可以访问的变量。

为什么在Python中不需要这样做?


这是文化。在Python中,您不会写入其他类的实例或类变量。在Java中,如果你真的想做的话,没有什么能阻止你做同样的事情。毕竟,你总是可以编辑类本身的来源来达到同样的效果。Python放弃了安全性的伪装,鼓励程序员负起责任。在实践中,这是非常好的工作。

如果出于某种原因想要模拟私有变量,则可以始终使用PEP8中的__前缀。Python将变量命名为EDCOX1(1),这样它们就不容易在包含它们的类之外进行编码(尽管如果你足够确定,你可以绕过它,就像你在工作时可以绕过Java的保护)一样。

按照同样的惯例,_前缀意味着远离,即使在技术上没有阻止你这样做。您不必使用其他类的变量,这些变量看起来像__foo_bar


python中的私有变量或多或少是一种黑客行为:解释器故意重命名变量。

1
2
3
4
5
class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

现在,如果您试图访问类定义之外的__var,它将失败:

1
2
3
4
 >>>x = A()
 >>>x.__var # this will return error:"A has no attribute __var"

 >>>x.printVar() # this gives back 123

但是你可以很容易地摆脱这个问题:

1
2
3
4
5
 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

您可能知道OOP中的方法是这样调用的:x.printVar() => A.printVar(x),如果A.printVar()可以访问x中的某个字段,那么这个字段也可以在A.printVar()之外访问……毕竟,函数是为可重用性而创建的,里面的语句没有特殊的权力。

当涉及编译器时,游戏是不同的(隐私是编译器级别的概念)。它知道带有访问控制修饰符的类定义,因此如果在编译时不遵循规则,它可能出错。


正如上面许多注释所正确提到的,让我们不要忘记访问修饰符的主要目标:帮助代码用户理解应该更改的内容和不应该更改的内容。当你看到一个私人领域时,你不会把它搞得一团糟。所以它主要是语法上的糖分,很容易在python中通过u和uuu实现。


"在Java中,我们已经学习了公共/私有/保护变量"。

"为什么在python中不需要?"

出于同样的原因,Java中不需要这样做。

你可以自由使用或者不使用privateprotected

作为一个Python和Java程序员,我发现EDCOX1 5和EDCOX1 6是非常重要的设计概念。但作为一个实际问题,在数以万计的Java和Python行中,我从来没有真正使用EDCOX1 5或EDCOX1 6。

为什么不呢?

我的问题是"保护谁?"

我团队中的其他程序员?他们有源头。当他们可以改变它时,受保护意味着什么?

其他团队的其他程序员?他们在同一家公司工作。他们可以——通过电话——找到源头。

客户?这是雇佣编程的工作(一般)。客户机(通常)拥有代码。

那么,究竟是谁——确切地说——我在保护它不受伤害?


下划线约定中存在私有变量的变体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return"Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:    

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

有一些细微的区别,但为了编程模式的思想纯洁性,它就足够好了。

有@private decorators的例子可以更紧密地实现这个概念,但是ymmv。可以说,还可以编写一个使用meta的类定义


python对私有标识符的支持有限,通过一个自动将类名前置到以两个下划线开头的任何标识符的特性。这在大多数情况下对程序员是透明的,但最终的效果是任何以这种方式命名的变量都可以用作私有变量。

更多信息请参见此处。

通常,与其他语言相比,Python的对象定向实现有点原始。但事实上,我喜欢这样。这是一个概念上非常简单的实现,非常适合语言的动态风格。


我唯一一次使用私有变量是当我向变量写入或从变量读取时需要做其他事情,因此我需要强制使用setter和/或getter。

正如前面所述,这同样适用于文化。我一直在做一些项目,在这些项目中,其他类变量的读写都是免费的。当一个实现被弃用时,识别使用该函数的所有代码路径需要更长的时间。当强制使用setter和getter时,可以很容易地编写调试语句来标识已调用不推荐使用的方法以及调用该方法的代码路径。

当您在一个任何人都可以编写扩展的项目中时,通知用户将在几个版本中消失的不推荐的方法,因此在升级时将模块损坏保持在最低程度是至关重要的。

所以我的答案是:如果您和您的同事维护一个简单的代码集,那么保护类变量并不总是必要的。如果您正在编写一个可扩展的系统,那么当对核心进行更改时,需要被使用代码的所有扩展捕获,这就变得非常必要。


如前所述,可以通过在变量或方法前面加下划线来指示它是私有的。如果你觉得这样不够,你可以一直使用property装饰。下面是一个例子:

1
2
3
4
5
6
7
8
9
class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
       """Getter for '_bar'."""
        return self._bar

这样,引用bar的某人或某物实际上引用了bar函数的返回值,而不是变量本身,因此可以访问它,但不能更改它。然而,如果有人真的想这样做,他们可以简单地使用_bar,并为其分配一个新的值。没有一种可靠的方法可以阻止某人访问您希望隐藏的变量和方法,正如已经反复说过的那样。但是,使用property是您可以发送的最清楚的消息,即变量不可编辑。property还可以用于更复杂的getter/setter/deleter访问路径,如这里所述:https://docs.python.org/3/library/functions.html属性


私有和受保护的概念非常重要。但是python——这只是一个原型化和快速开发的工具,开发中可用的资源有限,这就是为什么在python中一些保护级别没有那么严格。您可以在类成员中使用"uuuu",它工作正常,但看起来不够好-对此类字段的每个访问都包含这些字符。

另外,您可以注意到python oop概念并不完美,smaltalk或ruby更接近纯oop概念。即使是C或Java更接近。

python是非常好的工具。但它是简化的OOP语言。从语法和概念上简化。Python存在的主要目标是使开发人员能够以非常快的方式编写具有高抽象级别的易于阅读的代码。


很抱歉,伙计们"重修"了这条线,但我希望这能帮助一些人:

在Python 3中,如果你只想"封装"类属性,就像在Java中一样,你可以像这样做同样的事情:

1
2
3
4
5
6
7
8
9
10
class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

要实例化此操作,请执行以下操作:

1
2
ss = Simple("lol")
ss.show()

注意:print(ss.__s)将抛出一个错误。

实际上,python3会混淆全局属性名。把它变成一个"私有"属性,就像在Java中一样。该属性的名称仍然是全局的,但无法访问,就像其他语言中的私有属性一样。

但不要害怕。没关系。它也起作用。;)


Python没有任何私有变量,如C++或Java。如果需要,也可以随时访问任何成员变量。但是,在Python中不需要私有变量,因为在Python中公开类成员变量并不坏。如果需要封装成员变量,可以在以后使用"@property"进行封装,而不必破坏现有的客户机代码。

在Python中,单个下划线"uu"用于表示方法或变量不被视为类的公共API的一部分,并且API的这一部分可能在不同版本之间发生更改。您可以使用这些方法/变量,但如果使用此类的较新版本,代码可能会中断。

双下划线"uuuu"不表示"私有变量"。您可以使用它来定义变量,这些变量是"类局部的",并且不容易被子类覆盖。它会破坏变量名。

例如:

1
2
3
4
5
6
7
class A(object):
    def __init__(self):
        self.__foobar = None # will be automatically mangled to self._A__foobar

class B(A):
    def __init__(self):
        self.__foobar = 1 # will be automatically mangled to self._B__foobar

自我。foobar的名字会自动改为自我。在A级中,它被改为自我。在B级中,它被改为自我。因此,每个子类都可以定义自己的变量foobar,而不必覆盖其父变量。但是没有什么可以阻止您访问以双下划线开头的变量。但是,名称管理阻止您意外地调用这个变量/方法。

我强烈建议观看Raymond Hettingers在Pycon 2013上的"Pythons类开发工具包"(应该在YouTube上提供),它给出了一个很好的例子,说明了为什么以及如何使用@property和"_uuuuuuuuuu"实例变量。