679

我来自 Java 世界,正在阅读 Bruce Eckels 的Python 3 Patterns, Recipes and 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 中不需要这样做?

4

13 回答 13

1083

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

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

按照同样的约定,_前缀意味着远离,即使你在技术上没有被阻止这样做。你不要玩弄另一个类的看起来像__fooor的变量_bar

于 2009-10-29T02:01:29.063 回答
214

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()访问……毕竟,函数是为可重用而创建的,并没有赋予内部语句特殊的权力。

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

于 2015-09-26T22:06:06.073 回答
35

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

于 2014-11-17T05:17:25.737 回答
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。可以说,也可以编写一个使用元的类定义

于 2014-09-02T01:19:15.147 回答
15

如前所述,您可以通过在变量或方法前加上下划线来指示它是私有的。如果你觉得这还不够,你可以随时使用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

于 2017-04-14T04:19:26.777 回答
14

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 而无需覆盖其父变量。但是没有什么能阻止您访问以双下划线开头的变量。但是,名称修饰会阻止您偶然调用此变量/方法。

我强烈建议您观看来自 Pycon 2013 的 Raymond Hettinger 的Python 类开发工具包,它提供了一个很好的示例,为什么以及如何使用 @property 和“__”-instance 变量。

如果你已经暴露了公共变量并且你需要封装它们,那么你可以使用@property。因此,您可以从最简单的解决方案开始。您可以将成员变量公开,除非您有具体的理由不这样做。这是一个例子:

class Distance:
    def __init__(self, meter):
        self.meter = meter


d = Distance(1.0)
print(d.meter)
# prints 1.0

class Distance:
    def __init__(self, meter):
        # Customer request: Distances must be stored in millimeters.
        # Public available internals must be changed.
        # This would break client code in C++.
        # This is why you never expose public variables in C++ or Java.
        # However, this is python.
        self.millimeter = meter * 1000

    # In python we have @property to the rescue.
    @property
    def meter(self):
        return self.millimeter *0.001

    @meter.setter
    def meter(self, value):
        self.millimeter = meter * 1000

d = Distance(1.0)
print(d.meter)
# prints 1.0
于 2018-06-26T22:23:39.610 回答
11

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

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

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

于 2009-10-29T01:59:36.487 回答
11

“在 Java 中,我们学习了公共/私有/受保护变量”

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

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

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

private作为一名 Python和Java 程序员,我发现这protected是非常非常重要的设计概念。但实际上,在数万行 Java 和 Python 中,我从未真正使用过privateprotected.

为什么不?

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

我团队中的其他程序员?他们有来源。当他们可以更改时,受保护的含义是什么?

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

客户?它是(通常)雇佣编程。客户(通常)拥有代码。

那么,谁——确切地说——我在保护它?

于 2009-10-29T02:28:33.533 回答
7

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

如前所述,这又涉及到文化。我一直在从事可以免费读写其他类变量的项目。当一个实现被弃用时,识别使用该函数的所有代码路径需要更长的时间。当强制使用 setter 和 getter 时,可以轻松编写调试语句来识别已弃用的方法已被调用以及调用它的代码路径。

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

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

于 2013-04-28T17:22:44.923 回答
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 中一样。属性的名称仍然是全局的,但是以一种不可访问的方式,就像其他语言中的私有属性一样。

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

于 2017-08-15T01:30:22.153 回答
5

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

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

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

于 2011-07-17T06:00:14.223 回答
0

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

因此,根据经验,我更相信软件而不是人类。所以约定很好,但多重保护更好,比如访问管理(真正的私有变量)+源管理。

于 2020-08-14T16:33:11.563 回答
0

所以我是 Python 新手,但我有 C# 和 JavaScript 的背景。就功能而言,Python 感觉就像是两者的混合体。JavaScript 在这方面也很挣扎,这里的解决方法是创建一个闭包。这可以通过返回不同的对象来防止访问您不想公开的数据。

def print_msg(msg):
    # This is the outer enclosing function

    def printer():
        # This is the nested function
        print(msg)

    return printer  # returns the nested function


# Now let's try calling this function.
# Output: Hello
another = print_msg("Hello")
another()

https://www.programiz.com/python-programming/closure

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#emulating_private_methods_with_closures

于 2021-12-27T09:26:46.870 回答