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

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

544438 次浏览

深入Python,3.9。私有函数

严格来说,私有方法是在他们的课堂之外,只是不容易接近。里面什么都没有Python是真正私有的;在内部,私有方法的名称和属性被破坏和未破坏在飞行中让他们看起来用他们的名字无法接近。你可以访问的__parse方法MP3FileInfo类的名称_MP3FileInfo__parse承认这很有趣,然后承诺永远不要在真正的代码中这样做。私有方法对于a是私有的原因,但像许多其他事情在Python,他们的隐私是归根结底是一个惯例问题,而不是力量。

这并不是说你绝对不能绕过任何语言中成员的隐私(C++中的指针算术和. NET/Java中的反射)。

关键是,如果你无意中调用私有方法,你会得到一个错误。但如果你想搬起石头砸自己的脚,那就去做吧。

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

这只是语言设计中的一种选择。在某种程度上,它们是合理的。它们使您需要非常努力地尝试调用该方法,如果您真的非常需要它,您必须有一个非常好的理由!

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

通常使用的短语是“我们都是同意的成年人”。通过在单个下划线(不要暴露)或双下划线(隐藏)之前,你是在告诉类的用户,你希望该成员在某种程度上是“私有的”。但是,你相信其他人会负责任地行为并尊重这一点,除非他们有令人信服的理由(例如调试器和代码完成)。

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

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

例如:

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

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

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

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

from bar import *

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

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

今天,它可能只是Python的一件事我最爱

我喜欢身处这样一个平台,人们可以相互信任,不觉得有必要为自己的代码筑起密不透风的墙。在强封装语言中,如果一个API有bug,而你已经弄清楚了哪里出了问题,你可能仍然无法解决它,因为所需的方法是私有的。在Python中,态度是:“确定”。如果你认为你了解情况,也许你甚至已经读过,那么我们只能说“祝你好运!”。

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

一个私有函数的例子

import reimport 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 beginningmatched = re.match( '^self\.', function_call)if not matched:print 'This is a private function. Go away.'returnexcept: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 classself.private_function()
### End ###

对于Python 3.4,这是行为:

>>> class Foo:def __init__(self):passdef __privateMethod(self):return 3def 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.私有变量

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

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

obj._MyClass__myPrivateMethod()

我从C#迁移过来,起初对我来说也很奇怪,但过了一段时间,我意识到只有Python代码设计者对OOP的看法是不同的。

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

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

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

重要提示:

任何形式为__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 = namedef __print_my_name(self):print(self.name)        
        
tom = Cat()tom.__print_my_name() #Errortom._Cat__print_my_name() #Prints name