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

时间:2008-09-16 08:59:32

标签: python python-2.7 encapsulation information-hiding

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 "", line 1, in 
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 "", line 1, in 
AttributeError: MyClass instance has no attribute '__myPrivateMethod'

这里的一切看起来都很好;我们无法称呼它。事实上,它是“私人的”。嗯,实际上并非如此。在对象上运行 dir()会显示一个新的神奇方法,python为所有“私有”方法创造了神奇的方法。

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

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

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

封装这么多,嗯?

无论如何,我总是听说Python不支持封装,为什么要试试?是什么给了什么?

13 个答案:

答案 0 :(得分:549)

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

例如:

>>> 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 :(得分:205)

私人功能的例子

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 ###

答案 2 :(得分:147)

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

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

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

请记住,封装甚至与“安全”无关,或者让孩子远离草坪。这只是另一种模式,应该用于使代码库更容易理解。

答案 3 :(得分:139)

来自http://www.faqs.org/docs/diveintopython/fileinfo_private.html

  

严格来说,私人方法是   只是在课堂之外   不容易进入。没什么   Python是真正私密的;在内部,   私人方法和名称   属性被破坏和未被破坏   在飞行中让他们看起来   他们的名字无法进入。您   可以访问的__parse方法   MP3FileInfo类的名称   _MP3FileInfo__parse。承认这很有趣,然后承诺   从来没有,在真正的代码中做过。   私有方法是私有的   原因,但像许多其他事情一样   Python,他们的私密性是   最终是一个惯例,而不是   力。

答案 4 :(得分:87)

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

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

答案 5 :(得分:32)

这并不像你绝对不能用任何语言来解决成员的私密性(C ++中的指针算术,.NET / Java中的思考)。

重点是,如果您尝试偶然调用私有方法,则会出现错误。但如果你想用脚射击自己,那就去做吧。

编辑:你没有尝试通过OO封装来保护你的东西,是吗?

答案 6 :(得分:12)

class.__stuff命名约定让程序员知道他不打算从外部访问__stuff。名称错误使得任何人都不可能偶然做到这一点。

是的,你仍然可以解决这个问题,它比其他语言更容易(BTW也允许你这样做),但如果他关心封装,没有Python程序员会这样做。

答案 7 :(得分:12)

这只是其中一种语言设计选择。在某种程度上,他们是合理的。他们做到这一点你需要走得很远,试着去调用这个方法,如果你真的非常需要它,你必须有一个很好的理由!

当然可以负责任地使用调试钩子和测试作为可能的应用程序。

答案 8 :(得分:12)

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

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

from bar import *

但是,这是一种约定而不是语言约束。这些不是私人属性;它们可以被任何进口商引用和操纵。有人认为,由于这个原因,Python无法实现真正​​的封装。

答案 9 :(得分: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'

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

  

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

即使问题很严重,我也希望我的代码片段能够提供帮助。

答案 10 :(得分:2)

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

obj._MyClass__myPrivateMethod()

我已经从C#迁移了,起初它对我来说也很奇怪但是过了一段时间我才明白,只有Python代码设计师对OOP的思考方式才有所不同。

答案 11 :(得分:1)

重要更新(Python 3.4):

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

因此,__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

答案 12 :(得分:0)

  

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

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

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