Python在类中是否具有“私有”变量?


578

我来自Java世界,正在阅读Bruce Eckels的Python 3 Patterns,Recipes和Idioms

在阅读类时,它继续说在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中不需要这样做?


17
您的意思是实例变量,而不是变量,对吗?
PaulMcG,2009年

13
您应该检查以下属性:docs.python.org/library/functions.html#property。只需使用getter,您的变量将受到保护。
rubik

一个简短的答案就在这里。我希望这将有所帮助。
Premkumar chalmeti

Answers:


961

这是文化的。在Python中,您无需写入其他类的实例或类变量。在Java中,如果您真的想做的话,什么也不能阻止您做同样的事情-毕竟,您始终可以编辑类本身的源代码以达到相同的效果。Python放弃了这种安全性的幌子,并鼓励程序员负责。实际上,这非常好用。

如果出于某种原因要模拟私有变量,则始终可以使用PEP 8中__前缀。Python会像这样对变量名称进行修饰,以使它们在包含它们的类之外的代码中不易被看到(尽管只要有足够的决心,您可以解决它,就像您可以使用它来避开Java保护一样)。__foo

按照相同的约定,即使没有从技术上阻止您这样做_前缀也意味着不要离开。您不会玩弄看起来像__foo或的另一个类的变量_bar


14
这就说得通了。但是,我不认为Java中有任何方法可以访问类外部的私有变量(除非实际更改课程的源)。在那儿?
2009年

168
我倾向于使用python方式,但是我不认为java方式没有您所指出的那样毫无意义。快速声明一些私有内容会告诉某人阅读代码的一些非常有用的信息:仅在此类内部修改此字段。
Ned

67
@Omnipresent,您可以使用反射。
rapadura 2011年

76
让我弄清楚这一点,因此Python不实现公共属性或私有属性,因为“它冒充了安全性并鼓励程序员负责”,但是社区鼓励使用“ _”表示私有变量和方法吗?也许python应该明确地具有public和private no?它们的主要目的是告诉您应该使用什么API与类进行交互。它们充当文档,告诉您使用这些方法而不要使用这些方法。它们不是“安全性”,而是API文档,IDE甚至可以使用它来指导您!
PedroD

19
这是一个很好的答案,您的推理当然是正确的,但是我不同意一个方面。访问修饰符的目的从来都不是安全性。而是,它们是一种明确标定(并在很大程度上强制执行)类的哪些部分被视为内部的部分以及哪些暴露给该类的外部用户的方法。约定(文化)无疑是访问修饰符的有效替代方法,并且这两种方法各有优缺点,但是误以为语言水平的访问修饰符在任何意义上都是“安全的”,目的是误导性的。字。
devios1

159

python中的私有变量或多或少是一种技巧:解释器故意重命名该变量。

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

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


3
简而言之,这不是封装
watashiSHUN 2015年

2
我想知道PHP是否具有与愚蠢的私有变量类似的东西-因为私有变量在解释语言中实际上没有意义-我的意思是,如果x变量是私有的(如果未编译),它可以做什么优化?
NoBugs 2015年

1
我们如何随机化私有变量的模式?
crisron

@crisron同样的问题
IanS

5
@watashiSHUN“总之,这不是封装” =>是的。封装仅使用公共API,因此可以保护客户端代码免受实现更改的影响。命名约定是分辨API是什么以及实现是什么的一种非常有效的方法,关键是它可以正常工作。
bruno desthuilliers

30

正如上面的许多评论所正确提到的,我们不要忘记访问修饰符的主要目标:帮助代码用户理解应该更改的内容和不应该更改的内容。当您看到一个私有字段时,您不会把它弄乱。因此,主要是语法糖,可以通过_和__在Python中轻松实现。


4
我认为这与任何一点一样重要。在调试代码时(我知道,我很容易引入错误),知道哪些类可以更改成员变量,从而简化了调试过程。至少,如果变量受某个范围保护。类似的概念是C ++中的const函数。我知道成员变量没有在其中更改,因此我什至不认为该方法是变量设置错误的潜在原因。尽管它可以使类扩展/添加功能的后续开发成为可能,但是限制代码的可见性使其易于调试。
MrMas

18

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

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的类定义


14
我知道这对聚会来说很晚了,但是在搜索问题时会在Google上显示此链接。这并不能说明整个故事。__x由于类内部的变量A实际上是由编译器重写为的_A__x,因此它仍然不是完全私有的,仍然可以访问。
Zorf 2014年

1
当然,如果我看到一个名为的变量_A__x,则不会碰它。这可能会传染。我将摆脱困境。
Mateen Ulhaq'1

确信它不是真正的私人。但是在C ++和Java(等)中强制执行private的原理推理(即编译器优化)在Python中并没有真正存在,因此约定私有就足够了。Python约定通常是它相信您会在没有监督的情况下表现自己。(这是一个新手陷阱,但您要知道,请仔细考虑类的设计和使用情况)
Shayne

14

“在Java中,我们被教导有关公共/私有/保护变量”

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

出于同样的原因,在Java中不需要

您可以自由使用-或不使用privateand protected

作为一个Python和Java程序员,我发现,privateprotected是非常,非常重要的设计理念。但实际上,在成千上万的Java和Python行中,我从未真正使用过privateprotected

为什么不?

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

我团队中的其他程序员?他们有出处。受保护的人何时可以更改它意味着什么?

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

客户?(通常)是按需租用的程序。客户(通常)拥有代码。

那么,到底是谁在保护我?


118
-1:我同意Porculus。这与禁止访问或隐藏某些内容无关,而与隐式 API文档有关。开发人员以及编译器/解释器/代码检查器可以轻松地看到建议使用哪些成员,以及不应该触摸的成员(或至少要小心)。在大多数情况下,如果一个类或模块的所有成员都是公开的,那将是一团糟。考虑一下将私有/受保护/公共成员作为服务的区别:“嘿,这些成员很重要,而内部使用这些成员,可能对您没有用。”
Oben Sonne

7
@ S.Lott:我同意API文档具有更高的优先级,并且通常是传达API预期用途的唯一方法。但是有时成员的姓名和知名度(就私人/公共而言)足以说出自己的话。另外,我认为您的观点是,隐式文档的想法在没有API检查的编辑器中不能很好地工作,但是在IDE的代码完成中确实很有用。假设您已经读过API文档,它可以帮助您记住如何使用类。如果私有成员和公共成员之间没有区别,那么事情就不会那么聪明。
Oben Sonne

21
后期的讨论,但一切Porculus和OBEN正在请求这里的处理完全充分地公约“有下划线前缀为”(和没有伤害,该公约的执行编译器可能会导致)
ncoghlan

38
@ S.Lott我不是python家伙,所以我不会从这个角度发表评论。但是,作为Java开发人员,这确实是令人恐惧的建议。-1
dbyrne 2014年

11
哇。您完全错过了这一点,您给出了非常糟糕的建议,您侮辱了对此有异议的任何人,但是您仍然可以获得徽章和超过1000点的“答案”声望点。
埃里克·杜米尼尔

12

如前所述,您可以通过在变量或方法前加上下划线作为前缀来表明该变量或方法是私有的。如果您觉得不够,可以随时使用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


Django也对此表示赞赏。
babygame0ver

10

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

有关更多信息,请参见此处

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


是的 美丽之处在于,Python的元编程能力意味着您可以根据需要实际实现花哨的东西(还有一些实现@ private / @ protected / etc装饰器和东西的库。该死的我什至看到一个实现JS风格原型类的库。出于没有该死的理智的原因),但实际上并没有那么必要。.我有点讨厌“ python / js / whatever is lisp”模因,因为它几乎从来都不是真的,但是python确实共享与简单语法结合的'元编程印章“使用该语言
-Shayne

8

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

如前所述,这再次涉及文化。我一直在从事免费阅读和编写其他类变量的项目。一个实现被弃用时,识别使用该功能的所有代码路径的时间要长得多。当强制使用setter和getter时,可以很容易地编写一条调试语句来识别已调用了不赞成使用的方法以及调用该方法的代码路径。

当您在任何人都可以编写扩展的项目上时,通知用户有关已弃用的方法的信息,这些方法将在几个发行版中消失,因此对于将升级时模块的损坏降至最低至关重要。

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


7

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

另外,您会注意到python OOP概念并不完美,smaltalk或ruby更接近于纯OOP概念。甚至C#或Java都更接近。

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


对Private和Protected的重述是重要的,因为在静态编译的语言中,编译器可以创建对private方法的直接调用,但是必须依赖于查找表来查找公共方法。这根本不是动态语言的问题。最后,像C ++这样的语言对继承和方法解析都有影响。Python和Ruby具有非常相似的OO实现,因此比较是没有意义的。Smalltalk实际上没有公共/专用消息的概念。您可以随意添加私有作为类别,但纯粹是建议性的。
谢恩

为了进一步我的主张。从编码卫生的观点出发,是的,它们对于封装很重要,但对封装不是必需的,因此@private(etc)装饰器比任何东西都更具建议性,但由于private / public对于优化容器中的优化没有任何帮助非静态语言,无法像在Java或c这样的编译语言中那样在更深层次上实现
Shayne

7

抱歉,“恢复”线程,但是,我希望这会对某人有所帮助:

在Python3中,如果您只想“封装”类属性(例如在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将混淆全局属性名称。像在Java中一样,将其变为“私有”属性。该属性的名称仍然是全局的,但是以一种无法访问的方式,就像其他语言中的私有属性一样。

但是不要害怕。没关系 它也做这项工作。;)


2
自Python 1.5.2 IIRC起就存在此属性,并且它仍然不会阻止通过其整齐的名称访问属性。
bruno desthuilliers

4

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变量。


我要检查一下那个谈话。它是@property标准Python的一部分,还是特定于IDE?
bballdave025

自python 2.6起,它是标准的一部分。如果您应该使用旧版本,则仍然可以使用property内置函数,该函数自python 2.2开始提供
-Hatatister

0

实际上,您可以C#使用以下简单技巧来模拟吸气剂和吸气剂:

class Screen(object):

    def getter_setter_y(self, y, get=True):
        if get is True:
            Screen.getter_setter_y.value = y
        else:
            return Screen.getter_setter_y.value

     def getter_setter_x(self, x, get=True):
         if get is True:
             Screen.getter_setter_x.value = x
         else:
             return Screen.getter_setter_x.value

然后像这样使用它C#

scr = Screen()
scr.getter_setter_x(100)
value =  scr.getter_setter_x(0, get=False)
print (value)

这只是在函数中声明一个静态局部变量,该变量将扮演获取/设置的角色,因为这是通过get和set方法共享变量的唯一方法,而无需将其全局化为类或文件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.