为什么Python的私有方法事实上并不是真的私有?

Python为我们提供了在类中通过在名称前加上双下划线来创建"私有"方法和变量的能力,就像这样:__myPrivateMethod()。那么,如何解释这一点呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> 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"", line 1, in
AttributeError: MyClass instance has no attribute '__myPrivateMethod'
>>> dir(obj)
['_MyClass__myPrivateMethod', '__doc__', '__module__', 'myPublicMethod']
>>> obj._MyClass__myPrivateMethod()
this is private!!

这笔交易是什么? !

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

1
2
3
4
5
6
7
>>> class MyClass:
...     def myPublicMethod(self):
...             print 'public method'
...     def __myPrivateMethod(self):
...             print 'this is private!!'
...
>>> obj = MyClass()

我所做的是创建一个类,它有一个公共方法和一个私有方法并实例化它。

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

1
2
>>> obj.myPublicMethod()
public method

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

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

这里一切看起来都很好;我们不能打电话。事实上,它是"私人的"。其实不是。在对象上运行dir()揭示了python为所有"私有"方法神奇地创建的一个新方法。

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

这个新方法的名称总是下划线,后跟类名和方法名。

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

封装就这么多,是吧?

无论如何,我总是听说Python不支持封装,所以为什么还要尝试呢?到底发生了什么事?


名称置乱用于确保子类不会意外地覆盖其超类的私有方法和属性。它的设计不是为了防止外界故意进入。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> 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}

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


私有函数的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 begining
            matched = re.match( '^self\.', function_call )
            if not matched :
                print 'This is Private Function, Go Away'
                return
        except :
            print 'This is Private Function, Go Away'
            return

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

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

### End ###


当我第一次从Java转到Python时,我讨厌这样。吓死我了。

今天,它可能是我最喜欢Python的一点。

我喜欢在一个平台上,在这里人们彼此信任,并且不觉得他们需要围绕他们的代码建立不可穿透的墙。在强封装语言中,如果一个API有一个bug,而您已经知道出了什么问题,您可能仍然无法解决它,因为所需的方法是私有的。在Python中,态度是:"确定"。如果你认为你了解情况,也许你已经读过了,那么我们只能说"祝你好运!"

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


从http://www.faqs.org/docs/diveintopython/fileinfo_private.html

Strictly speaking, private methods are
accessible outside their class, just
not easily accessible. Nothing in
Python is truly private; internally,
the names of private methods and
attributes are mangled and unmangled
on the fly to make them seem
inaccessible by their given names. You
can access the __parse method of the
MP3FileInfo class by the name
_MP3FileInfo__parse. Acknowledge that this is interesting, then promise to
never, ever do it in real code.
Private methods are private for a
reason, but like many other things in
Python, their privateness is
ultimately a matter of convention, not
force.


常用的短语是"我们都是自愿的成年人"。通过在前加一个下划线(不要暴露)或两个下划线(隐藏),您可以告诉类的用户,您希望成员在某种程度上是"私有的"。然而,你相信其他人的行为是负责任的,并且尊重他们的行为,除非他们有令人信服的理由不这样做(例如调试器,代码完成)。

如果您确实需要一些私有的东西,那么您可以在扩展中实现它(例如,在C语言中实现CPython)。然而,在大多数情况下,您只需要学习python的方法即可。


这并不是说您绝对不能在任何语言(c++中的指针算术、. net / java中的反射)中避开成员的私密性。

关键是,如果不小心调用了private方法,就会得到一个错误。但是如果你想搬起石头砸自己的脚,尽管去做吧。

编辑:您不会试图通过oo封装来保护您的东西,是吗?


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

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

1
from bar import *

然而,这是一种约定,而不是语言约束。这些不是私有属性;它们可以被任何进口商引用和操纵。有些人认为,正因为如此,Python不能实现真正的封装。


这只是语言设计的选择之一。在某种程度上,它们是合理的。他们这样做了,所以你需要走很远的路去尝试调用这个方法,如果你真的非常需要它,你一定有一个很好的理由!

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


class.__stuff命名约定让程序员知道他不打算从外部访问__stuff。"乱改"这个名字使任何人都不太可能偶然地做这件事。

的确,您仍然可以解决这个问题,它甚至比其他语言更简单(顺便说一句,其他语言也允许您这样做),但是如果Python程序员关心封装,就不会这样做。


这是Python 3.4的行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
>>> 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'

https://docs.python.org/3/tutorial/classes.html#tut-private

Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger.

即使这个问题很老了,我希望我的片段能有所帮助。


私有方法和属性最重要的关注点是告诉开发人员不要在类之外调用它,这就是封装。人们可能会误解封装的安全性。当有人故意使用您提到的那种语法时(如下所示),您不希望封装。

1
obj._MyClass__myPrivateMethod()

我是从c#迁移过来的,一开始我也觉得很奇怪,但过了一段时间,我意识到只有Python代码设计人员考虑OOP的方式不同。


Why are Python's 'private' methods not actually private?

据我所知,他们不可能是私人的。如何保护隐私?

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