ChatGPT解决这个技术问题 Extra ChatGPT

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

我来自 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 中不需要这样做?

您的意思是实例变量,而不是类变量,对吗?
您应该检查属性:docs.python.org/library/functions.html#property。只需使用 getter,您的变量就会受到保护。
一个简短而清晰的答案是here。我希望这将有所帮助。

M
Madacol

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

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

按照同样的约定,_ 前缀表示即使在技术上没有阻止您这样做。您不会使用看起来像 __foo_bar 的另一个类的变量。


那讲得通。但是,我认为java中没有任何方法可以访问类外的私有变量(除了实际更改类的源)。在那儿?
我倾向于更喜欢 python 方式,但我不认为 java 方式像你所说的那样毫无意义。快速声明一些私有的东西会告诉阅读代码的人一些非常有用的东西:这个字段只在这个类中被修改过。
@Omnipresent,您可以使用反射。
让我直截了当,所以 Python 没有实现公共或私有属性,因为“它是一种安全的伪装并鼓励程序员负责”,但是社区鼓励使用“_”来表示私有变量和方法?也许python应该明确地有公共和私人没有?它们的主要目的是告诉您应该使用什么 API 来与类进行交互。它们充当文档,告诉您使用这些方法而不是使用这些方法。它们不是“安全的幌子”,它们是 API 文档,IDE 甚至可以使用它来指导您!
这是一个很好的答案,你的推理当然是有效的,但我不同意一个方面。访问修饰符的目的从来都不是安全性。相反,它们是一种明确划分(并且在很大程度上强制执行)类的哪些部分被认为是内部的以及哪些部分暴露给该类的外部用户的一种方法。约定(文化)当然是访问修饰符的有效替代方法,两种方法各有利弊,但语言级访问修饰符以任何方式旨在以通常意义上的“安全”为目的是误导性的。单词。
w
wjandrea

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 中的某个字段,则该字段也可以在 outside A.printVar()...之后访问总而言之,函数是为了可重用性而创建的,里面的语句没有特殊的权力。

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


简而言之,这不是封装
我想知道 PHP 是否与其愚蠢的私有变量有类似的东西——因为私有变量在解释语言中并没有真正意义——我的意思是知道 x 变量是私有的,如果它没有被编译,它可以做什么优化?
我们如何随机化私有变量的模式?
@watashiSHUN “简而言之,这不是封装” => 是的。封装仅使用公共 API,因此客户端代码不受实现更改的影响。命名约定是一种非常有效的方式来说明什么是 API,什么是实现,关键是它可以正常工作。
封装与是否可以通过复杂的方法访问私有变量无关。任何人都可以在具有“私有变量”的 C++ 中直接访问内存。 Python 还可以访问“私有变量”。大不了。
A
Ardavan

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


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

下划线约定中有不同的私有变量。

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。可以说,也可以编写一个使用元的类定义


我意识到这对聚会来说已经很晚了,但是当谷歌搜索这个问题时,这个链接会出现在谷歌上。这并不能说明全部。 __x 作为类 A 中的变量实际上被编译器重写为 _A__x,它仍然不是完全私有的,仍然可以访问。
当然,如果我看到一个名为 _A__x 的变量,我不会去碰它。它可能具有传染性。我会远离它。
确定它不是真正的私人。但是 C++ 和 Java (etc) 中硬强制私有的原理推理,编译器优化,在 Python 中并不存在,所以按惯例私有就足够了。 Python 约定通常是它相信你会在没有监督的情况下表现自己。 (而且这是一个新手陷阱,但你知道,请考虑课程设计和消费)
@Shayne您无法根据访问说明符在C++中进行优化。您可以使用带有一些模板技巧的标头定义来访问类的私有成员,而无需诉诸 C 风格的强制转换。请参阅 Johannes Schaub 对 stackoverflow.com/questions/424104/… 的回答。访问说明符实际上只是为了保护您自己。
你绝对可以。私有成员可以优化为直接相关的 JMP optcode,而公共成员需要 vtables,因为它们可以在外部调用。是的,存在变通方法,但它们要求访问来自对象内部。
H
Hatatister

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's class development toolkit,它提供了一个很好的示例,为什么以及如何使用 @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 = value * 1000

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

我要去看看那个谈话。 @property 是标准 Python 的一部分,还是特定于 IDE?
它是自 python 2.6 以来标准的一部分。如果您应该使用旧版本,仍然可以使用 property 内置函数,该函数从 python 2.2 开始可用
很抱歉 4 年后才提到这一点,但这让我很烦。在您的最后一个示例中,当您编写 setter 时,我认为您编写的是 meter 而不是 value
你说的对。我纠正了错误。
C
Cleb

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

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

有关详细信息,请参阅 here

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


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

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

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

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

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

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

为什么不?

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

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

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

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

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


-1:我同意 Porculus。这不是关于禁止访问或隐藏某些东西,而是关于隐式 API 文档。开发人员以及编译器/解释器/代码检查器可以轻松查看推荐使用哪些成员以及不应该触及哪些成员(或至少要小心)。在大多数情况下,如果一个类或模块的所有成员都是公共的,那将是一个可怕的混乱。考虑作为服务的私有/受保护/公共成员的区别,说:“嘿,这些成员很重要,而那些在内部使用,可能对你没用。”
讨论晚了,但是 Porculus 和 Oben 在这里请求的所有内容都通过“带下划线前缀”约定完美地处理(并且没有编译器强制执行该约定可能造成的损害)
@ncoghlan 这些观点遍布互联网和大量 Python 出版物。不让他们成为圣经。许多人认为接口的编译器强制执行、强大的数据封装和类型具有很大的好处。 “隐私无助于封装设计。”同意不同意。 “Private 只是……帮助那些无法阅读文档或拒绝遵循文档的人。”再说一次,傻。正如我们都同意的那样,高级、松散类型的语言和低级、强类型的语言都有好处。它们都是工具箱中的工具!
@S.Lott 我不是 python 人,所以我不会从这个角度发表评论。然而,作为一名 java 开发人员,这确实是一个可怕的建议。 -1
哇。你完全没有抓住重点,你给出了一个非常糟糕的建议,你侮辱了在这一点上不同意你的任何人,但你仍然会因为这个“答案”而获得徽章和超过 1000 的声望点。
B
Bulwersator

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

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

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

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


N
Niall Byrne

对不起,“复活”线程,但是,我希望这会对某人有所帮助:

在 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 中一样。属性的名称仍然是全局的,但是以一种不可访问的方式,就像其他语言中的私有属性一样。

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


这自 Python 1.5.2 IIRC 以来就已经存在,并且它仍然不会阻止通过其损坏的名称访问该属性。
属性名称不是“全局的”。
u
user711294

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

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

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


私有和受保护的重要之处在于,在静态编译的语言中,编译器可以创建对私有方法的直接调用,但必须依赖于公共方法的查找表。这根本不是动态语言的问题。最后,像 C++ 这样的语言对继承和方法解析有影响。 Python 和 Ruby 的 OO 实现非常相似,所以比较没有意义。 Smalltalk 实际上没有公共/私人消息的概念。您可以自由地将私人添加为一个类别,但这纯粹是建议性的。
进一步说明我的主张。从编码卫生的角度来看,是的,它们对于封装很重要,但它不是必需的,因此 @private (等)装饰器比任何东西都更具咨询性,但由于私有/公共对优化没有任何用处非静态语言,它不像java或c这样的编译语言那样在深层实现
A
Alan Allen

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

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


j
julemand101

所以我是 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


我不确定这与问题有什么关系......这是关于类中的私有属性,而不是函数中的闭包......
不要在 Python 中这样做。在 Python 中,您只需使用前导下划线来表示属性是“私有的”,没有访问修饰符。不要用闭包替换你的类。