Python在类中有“私有”变量吗?

时间:2009-10-29 01:54:19

标签: python class private

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

在阅读类时,接着说在Python中没有必要声明实例变量。你只需在构造函数中使用它们,然后繁荣,它们就在那里。

例如:

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的值。

例如:

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

在Java中,我们学习了有关公共/私有/受保护变量的知识。这些关键字是有意义的,因为有时你需要类中的变量,类外没有人可以访问。

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

12 个答案:

答案 0 :(得分:860)

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

如果您出于某种原因想要模拟私有变量,可以始终使用PEP 8中的__前缀。 Python会破坏__foo等变量的名称,以便在包含它们的类之外的代码中不易看到它们(尽管如果你足够坚定,可以绕过它)像你一样可以绕过Java的保护,如果你工作的话。)

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

答案 1 :(得分:128)

python中的私有变量或多或少是一个hack:解释器故意重命名变量。

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

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

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

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

但你可以轻松逃脱:

 >>>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() ...毕竟,为可重用性创建了函数,内部语句没有特殊的权力。

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

答案 2 :(得分:24)

正如上面许多评论中正确提到的那样,让我们​​不要忘记访问修饰符的主要目标:帮助代码用户理解应该更改的内容以及不应该更改的内容。当你看到私人田地时,你就不会乱搞它。所以它主要是语法糖,很容易通过_和__。

在Python中实现

答案 3 :(得分:20)

“在java中,我们学过公共/私有/受保护变量”

“为什么在python中不需要?”

出于同样的原因,它在Java中不是 required

您可以自由使用 - 或者不使用privateprotected

作为Python和Java程序员,我发现privateprotected是非常非常重要的设计概念。但实际上,在数以万计的Java和Python行中,我从来没有实际使用privateprotected

为什么不呢?

这是我的问题“受到谁保护?”

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

其他团队的其他程序员?他们在同一家公司工作。他们可以 - 通过电话 - 获取消息来源。

客户端?这是一种招聘工作(一般)。客户(通常)拥有代码。

那么,究竟是谁 - 我是在保护它吗?

答案 4 :(得分:14)

下划线约定中有一些私有变量。

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装饰器的例子更贴切地实现了这个概念,但YMMV。可以说,人们也可以写一个使用meta

的类定义

答案 5 :(得分:9)

Python对私有标识符的支持有限,通过自动将类名添加到以两个下划线开头的任何标识符的功能。在大多数情况下,这对程序员来说是透明的,但实际效果是任何以这种方式命名的变量都可以用作私有变量。

有关详情,请参阅here

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

答案 6 :(得分:9)

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

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#property

答案 7 :(得分:8)

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

如上所述,这又归于文化。我一直致力于读取和编写其他类变量的项目是免费的。当一个实现被弃用时,需要花费更长的时间来识别使用该功能的所有代码路径。当强制使用setter和getter时,可以很容易地编写一个debug语句来识别已经调用过的方法以及调用它的代码路径。

当你所在的项目中,任何人都可以编写扩展程序,通知用户有关在几个版本中消失的弃用方法因此对于在升级时将模块破坏保持在最低限度至关重要。

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

答案 8 :(得分:7)

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

另外,你可以注意到python OOP概念并不完美,smaltalk或ruby更接近纯OOP概念。甚至C#或Java也更接近。

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

答案 9 :(得分:5)

对不起家伙们来说&#34;复活&#34;线程,但是,我希望这会对某人有所帮助:

在Python3中,如果你只想&#34;封装&#34;类的属性,就像在Java中一样,你可以像这样做:

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

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

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

请注意:print(ss.__s)会抛出错误。

实际上,Python3将模糊全局属性名称。把它变成私人&#34;属性,就像在Java中一样。属性的名称仍然是全局的,但是以不可访问的方式,就像其他语言中的私有属性一样。

但不要害怕它。这没关系。它也完成了这项工作。 ;)

答案 10 :(得分:3)

Python没有像C ++或Java那样的任何私有变量。如果需要,您也可以随时访问任何成员变量。但是,在Python中不需要私有变量,因为在Python中公开类成员变量也不错。如果需要封装成员变量,则可以稍后使用“ @property”来实现,而无需破坏现有的客户端代码。

在python中,单个下划线“ _”用于表示方法或变量不被视为类的公共api的一部分,并且该api的这一部分可以在不同版本之间进行更改。您可以使用这些方法/变量,但如果使用此类的较新版本,则代码可能会中断。

双下划线“ __”并不表示“私有变量”。您可以使用它来定义“局部类”的变量,并且这些变量不能轻易被子类覆盖。它会破坏变量名称。

例如:

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

self .__ foobar的名称在A类中自动更改为self._A__foobar。在B类中,其名称更改为self._B__foobar。因此,每个子类都可以定义自己的变量__foobar而不覆盖其父变量。但是没有什么可以阻止您访问以双下划线开头的变量。但是,名称修改可防止您偶然调用此变量/方法。

我强烈建议观看Raymond Hettingers讲述Pycon 2013上的“ Pythons类开发工具包”(应该在Youtube上提供),该示例很好地说明了为什么以及如何使用@property和“ __”-instance变量。 / p>

答案 11 :(得分:0)

关于源(更改访问权限,从而绕过Java或C ++之类的语言封装): 您并不总是拥有源代码,即使您拥有源代码,也并非总是如此,源代码是由一个系统管理的,该系统仅允许某些程序员(在专业环境中)访问源代码。通常,每个程序员都对某些类负责,因此知道他能做什么和不能做什么。源代码管理器还锁定正在修改的源代码,当然,还可以管理程序员的访问权限。

因此,根据经验,我对软件的信任程度要高于对人类的信任。因此约定是好的,但是多重保护会更好,例如访问管理(真实私有变量)+源管理。