756

Python 使我们能够通过在名称前添加双下划线来在类中创建“私有”方法和变量,例如:__myPrivateMethod(). 那么,如何解释这一点

>>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

>>> obj.myPublicMethod()
public method

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

>>> obj._MyClass__myPrivateMethod()
this is private!!

这是怎么回事?!

我会为那些不太明白的人解释一下。

>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

我用公共方法和私有方法创建了一个类并实例化它。

接下来,我调用它的公共方法。

>>> obj.myPublicMethod()
public method

接下来,我尝试调用它的私有方法。

>>> obj.__myPrivateMethod()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

这里的一切看起来都不错;我们无法调用它。事实上,它是“私人的”。嗯,实际上并非如此。在对象上运行dir()揭示了 Python 为所有“私有”方法神奇地创建的新神奇方法。

>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']

这个新方法的名字总是一个下划线,然后是类名,然后是方法名。

>>> obj._MyClass__myPrivateMethod()
this is private!!

封装这么多,嗯?

无论如何,我总是听说 Python 不支持封装,那么为什么还要尝试呢?是什么赋予了?

4

12 回答 12

662

名称加扰用于确保子类不会意外覆盖其超类的私有方法和属性。它的设计目的不是为了防止从外部故意访问。

例如:

>>> class Foo(object):
...     def __init__(self):
...         self.__baz = 42
...     def foo(self):
...         print self.__baz
...     
>>> class Bar(Foo):
...     def __init__(self):
...         super(Bar, self).__init__()
...         self.__baz = 21
...     def bar(self):
...         print self.__baz
...
>>> x = Bar()
>>> x.foo()
42
>>> x.bar()
21
>>> print x.__dict__
{'_Bar__baz': 21, '_Foo__baz': 42}

当然,如果两个不同的类具有相同的名称,它就会崩溃。

于 2008-09-16T10:06:07.943 回答
214

私有函数示例

import re
import inspect

class MyClass:

    def __init__(self):
        pass

    def private_function(self):
        try:
            function_call = inspect.stack()[1][4][0].strip()

            # See if the function_call has "self." in the beginning
            matched = re.match( '^self\.', function_call)
            if not matched:
                print 'This is a private function. Go away.'
                return
        except:
            print 'This is a private function. Go away.'
            return

        # This is the real function, only accessible inside the class #
        print 'Hey, welcome in to the function.'

    def public_function(self):
        # I can call a private function from inside the class
        self.private_function()

### End ###
于 2010-06-30T08:24:05.460 回答
201

当我第一次从 Java 转向 Python 时,我讨厌这个。把我吓死了。

今天,它可能只是我最喜欢 Python 的一件事。

我喜欢在一个平台上,人们相互信任,并且不觉得他们需要在他们的代码周围建造坚不可摧的墙。在强封装的语言中,如果 API 有错误,并且您已经找出问题所在,您可能仍然无法解决它,因为所需的方法是私有的。在 Python 中,态度是:“确定”。如果您认为您了解情况,也许您甚至已经阅读过它,那么我们只能说“祝您好运!”。

请记住,封装与“安全”或让孩子远离草坪甚至没有微弱的关系。它只是另一种模式,应该用于使代码库更易于理解。

于 2009-12-22T23:25:42.737 回答
154

潜入 Python,3.9。私人功能

严格来说,私有方法可以在其类之外访问,只是不容易访问。Python 中没有什么是真正私有的。在内部,私有方法和属性的名称会在运行中被修改和取消修改,以使它们看起来无法通过它们的给定名称访问。您可以通过名称 _MP3FileInfo__parse 访问 MP3FileInfo 类的 __parse 方法。承认这很有趣,然后承诺永远不会在实际代码中这样做。私有方法是私有的是有原因的,但就像 Python 中的许多其他东西一样,它们的私有性最终是一个约定问题,而不是强制问题。

于 2008-09-16T09:03:45.080 回答
103

常用的短语是“我们在这里都是同意的成年人”。通过添加一个单下划线(不要暴露)或双下划线(隐藏),您是在告诉您班级的用户您希望该成员以某种方式成为“私人”。但是,您相信其他所有人都会负责任地行事并尊重这一点,除非他们有令人信服的理由不这样做(例如,调试器和代码完成)。

如果您确实必须拥有私有的东西,那么您可以在扩展中实现它(例如,在 C 中用于CPython)。然而,在大多数情况下,您只需学习Pythonic的做事方式。

于 2008-09-16T09:33:18.317 回答
34

这并不是说您绝对无法解决任何语言中成员的隐私问题(C++ 中的指针算术和 .NET/Java 中的反射)。

关键是,如果您尝试不小心调用私有方法,则会出现错误。但是,如果您想在脚上开枪,那就去做吧。

您不会尝试通过 OO 封装来保护您的东西,是吗?

于 2008-09-16T09:04:57.030 回答
16

重要的提示:

任何形式的标识符__name(至少两个前导下划线,最多一个尾随下划线)都被公开替换为_classname__name,其中classname是去掉前导下划线的当前类名。

因此,__name不能直接访问,但可以作为_classname__name.

这并不意味着您可以保护您的私人数据,因为它可以通过更改变量的名称轻松访问。

来源:

官方文档中的“私有变量”部分:https ://docs.python.org/3/tutorial/classes.html#tut-private

例子

class Cat:
    def __init__(self, name='unnamed'):
        self.name = name
    def __print_my_name(self):
        print(self.name)
        
        
tom = Cat()
tom.__print_my_name() #Error
tom._Cat__print_my_name() #Prints name
于 2020-11-24T11:16:38.667 回答
12

这只是这些语言设计选择之一。在某种程度上,他们是有道理的。他们做到了,所以你需要不遗余力地尝试调用该方法,如果你真的非常需要它,你一定有一个很好的理由!

调试钩子和测试作为可能的应用程序浮现在脑海中,当然要负责任地使用。

于 2008-09-16T09:09:32.430 回答
12

当模块属性名称以单个下划线开头(例如_foo)时,存在类似的行为。

使用该方法时,这样命名的模块属性不会复制到导入模块中from*,例如:

from bar import *

但是,这是一种约定,而不是语言限制。这些不是私有属性;它们可以被任何进口商引用和操作。有人争辩说,正因为如此,Python 无法实现真正​​的封装。

于 2008-09-17T04:29:45.137 回答
4

在 Python 3.4 中,行为如下:

>>> class Foo:
        def __init__(self):
                pass
        def __privateMethod(self):
                return 3
        def invoke(self):
                return self.__privateMethod()


>>> help(Foo)
Help on class Foo in module __main__:

class Foo(builtins.object)
 |  Methods defined here:
 |
 |  __init__(self)
 |
 |  invoke(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

 >>> f = Foo()
 >>> f.invoke()
 3
 >>> f.__privateMethod()
 Traceback (most recent call last):
   File "<pyshell#47>", line 1, in <module>
     f.__privateMethod()
 AttributeError: 'Foo' object has no attribute '__privateMethod'

9.6 开始。私有变量

请注意,修改规则主要是为了避免事故。仍然可以访问或修改被认为是私有的变量。这甚至在特殊情况下很有用,例如在调试器中。

于 2016-08-24T12:31:44.700 回答
4

关于私有方法和属性最重要的问题是告诉开发人员不要在类之外调用它,这就是封装。人们可能会误解封装的安全性。当一个人故意使用像你提到的那样(下面)的语法时,你不想要封装。

obj._MyClass__myPrivateMethod()

我已经从 C# 迁移过来了,起初这对我来说也很奇怪,但过了一段时间我想到只有 Python 代码设计人员对 OOP 的看法不同。

于 2018-04-26T22:37:29.203 回答
1

为什么 Python 的“私有”方法实际上不是私有的?

据我了解,他们不能是私人的。如何保护隐私?

显而易见的答案是“只能通过self”访问私有成员,但这不起作用——self这在 Python 中并不特殊。它只不过是函数第一个参数的常用名称。

于 2019-01-14T10:15:52.000 回答