为什么 Python 的“私有”方法实际上并不是私有的?
- 2024-12-11 08:47:00
- admin 原创
- 128
问题描述:
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 不支持封装,那为什么还要尝试呢?到底是怎么回事?
解决方案 1:
名称扰乱用于确保子类不会意外覆盖其超类的私有方法和属性。它并非旨在阻止外部故意访问。
例如:
>>> 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}
当然,如果两个不同的类有相同的名称,它就会崩溃。
解决方案 2:
当我第一次从 Java 转到 Python 时,我讨厌这一点。它吓死我了。
如今这也许就是我最喜欢Python 的一个原因了。
我喜欢在一个平台上,人们相互信任,不觉得需要在代码周围建立坚不可摧的墙壁。在强封装语言中,如果 API 有错误,并且您已经弄清楚了问题所在,但您可能仍然无法解决它,因为所需的方法是私有的。在 Python 中,态度是:“当然”。如果您认为您了解情况,也许您甚至已经阅读过它,那么我们只能说“祝你好运!”。
请记住,封装与“安全”或阻止孩子乱动草坪没有任何关系。它只是另一种使代码库更易于理解的模式。
解决方案 3:
私有函数示例
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 ###
解决方案 4:
摘自《深入 Python》,3.9。私有函数:
严格来说,私有方法可以在类外部访问,只是不容易访问。Python 中没有任何东西是真正私有的;在内部,私有方法和属性的名称被动态地破坏和恢复,使它们看起来无法通过给定的名称访问。您可以通过名称 _MP3FileInfo__parse 访问 MP3FileInfo 类的 __parse 方法。承认这很有趣,然后保证永远不会在实际代码中这样做。私有方法是私有的,这是有原因的,但与 Python 中的许多其他东西一样,它们的私有性最终是一种惯例,而不是强制。
解决方案 5:
常用的短语是“我们都是成年人”。通过在前面添加单下划线(不暴露)或双下划线(隐藏),您是在告诉类的用户,您希望该成员以某种方式“私有”。但是,您相信其他人都会负责任地行事并尊重这一点,除非他们有令人信服的理由不这样做(例如,调试器和代码完成)。
如果你确实需要一些私有的东西,那么你可以在扩展中实现它(例如,在CPython中使用 C 语言)。然而,在大多数情况下,你只是学习Pythonic的做事方式。
解决方案 6:
这并不意味着你绝对无法绕过任何语言中成员的隐私性(C++ 中的指针算法和 .NET/Java 中的反射)。
关键在于,如果您意外尝试调用私有方法,则会出错。但如果您想搬起石头砸自己的脚,那就去做吧。
您不会尝试通过 OO 封装来保护您的资料吧?
解决方案 7:
重要提示:
任何形式__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 = name
def __print_my_name(self):
print(self.name)
tom = Cat()
tom.__print_my_name() #Error
tom._Cat__print_my_name() #Prints name
解决方案 8:
当模块属性名称以单下划线开头(例如 _foo)时也会存在类似的行为。
使用此方法时,如此命名的模块属性将不会被复制到导入模块中from*
,例如:
from bar import *
然而,这只是惯例,而不是语言约束。这些不是私有属性;任何导入器都可以引用和操作它们。有人认为,正因为如此,Python 无法实现真正的封装。
解决方案 9:
这只是语言设计选择之一。从某种程度上来说,它们是合理的。它们让你需要费很大力气才能尝试调用该方法,如果你真的那么需要它,那你一定有很好的理由!
调试挂钩和测试被认为是可能的应用程序,当然要负责任地使用。
解决方案 10:
关于私有方法和属性最重要的问题是告诉开发人员不要在类外调用它,这就是封装。人们可能会误解封装与安全性。当人们故意使用您提到的语法(如下)时,您不需要封装。
obj._MyClass__myPrivateMethod()
我已经从 C# 迁移过来,一开始对我来说也很奇怪,但过了一段时间后,我发现只有 Python 代码设计人员对 OOP 的看法不同。
解决方案 11:
对于 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. 私有变量:
请注意,修改规则主要是为了避免意外;仍然可以访问或修改被视为私有的变量。这甚至在特殊情况下也很有用,例如在调试器中。
解决方案 12:
为什么 Python 的“私有”方法实际上并不是私有的?
据我了解,它们无法保密。如何强制执行隐私?
显而易见的答案是“私有成员只能通过 ”访问self
,但这样做不行——self
在 Python 中并不特殊。它只不过是函数第一个参数的常用名称。