English 中文(简体)
为什么Python的私有方法实际上不是私有的?
原标题:
  • 时间:2008-09-16 08:59:32
  •  标签:

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不支持封装,那么为什么还要尝试呢?什么东西?

最佳回答

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

例如:

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

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

问题回答

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

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

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

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

Example of a private function

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

来自深入Python,3.9。专用功能

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中的反射)。

关键是,如果你试图意外地调用私有方法,就会出现错误。但如果你想开枪打自己的脚,那就去做吧。

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

Important note:

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

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

这并不意味着您可以保护您的私人数据,因为通过更改变量的名称可以很容易地访问这些数据。

Source:

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

Example

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

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

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

from bar import *

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

这只是其中一种语言设计选择。在某种程度上,它们是合理的。他们做到了,所以你需要非常努力地尝试和调用这个方法,如果你真的非常需要它,你必须有一个很好的理由!

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

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

obj._MyClass__myPrivateMethod()

我已经从C#迁移过来了,一开始这对我来说也很奇怪,但过了一段时间,我发现只有Python代码设计者对OOP的思考方式不同。

对于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。私有变量

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

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

据我所知,他们不能是私人的。隐私如何被强制执行?

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





相关问题