为什么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 "", 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()
我在那里所做的是创建一个带有公共方法和私有方法的类,并将其实例化。
接下来,我将其称为public方法。
>>> 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不支持封装,那么为什么还要尝试呢?是什么赋予了?
解决方案
回答
来自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 ++中的指针算术,.NET / Java中的Reflections)。
关键是,如果我们尝试意外调用私有方法,则会出错。但是,如果我们想用脚射击自己,那就继续吧。
编辑:我们不会尝试通过OO封装来保护自己的东西,是吗?
回答
" class .__ stuff"命名约定使程序员知道他不是要从外部访问" __stuff"。改名这个名字使任何人都不太可能偶然地这样做。
没错,我们仍然可以解决此问题,它甚至比其他语言还容易(顺便说一句,BTW也允许我们执行此操作),但是如果Python程序员关心封装,那么他将不会这样做。
回答
这只是这些语言设计选择之一。在某种程度上,它们是合理的。他们做到了,所以我们需要走很长的路才能尝试调用该方法,如果真的很需要它,则必须有充分的理由!
我想到了调试钩子和测试作为可能的应用程序,它们当然是负责任地使用的。
回答
常用的短语是"我们在这里都是成年人"。通过在单个下划线(不要公开)或者双下划线(隐藏)前面加上,可以告诉班级用户我们希望该成员以某种方式成为"私人"。但是,除非其他人有充分的理由不这样做(例如调试器,代码完成),否则我们将信任其他所有人的行为负责并尊重。
如果我们确实必须拥有私有的东西,则可以在扩展中实现它(例如在C for CPython中)。但是,在大多数情况下,我们只是学习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}
当然,如果两个不同的类具有相同的名称,它就会崩溃。
回答
当模块属性名称以单个下划线(例如_foo)开头时,存在类似的行为。
当使用from *
方法时,这样命名的模块属性将不会复制到导入模块中,例如:
from bar import *
但是,这是约定,不是语言限制。这些不是私有属性。它们可以被任何进口商引用和操纵。有人认为,因此,Python无法实现真正的封装。
回答
当我第一次从Java到Python时,我讨厌这一点。吓死我了。
今天,它可能只是我最喜欢Python的一件事。
我喜欢在一个平台上,人们可以互相信任,而不必觉得自己需要围绕自己的代码构建难以穿透的墙。在高度封装的语言中,如果API有错误,并且我们找出了问题所在,则可能仍无法解决它,因为所需的方法是私有的。在Python中,态度是:"确定"。如果我们认为自己了解情况,也许我们甚至已经读过它,那么我们所能说的就是"祝我们好运!"。
请记住,封装甚至与"安全性"或者使孩子远离草坪都不是很弱的关系。这只是使代码库更易于理解的另一种模式。
回答
私有功能的例子
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 ###