Python为我们提供了在类中创建“private”方法和变量的能力,方法是在名称前加上双下划线,例如:__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不支持封装,所以为什么还要尝试呢?有什么好处?


当前回答

常用的短语是“我们都是成年人”。通过在前面加上一个下划线(不要暴露)或双下划线(隐藏),您可以告诉类的用户您希望成员以某种方式“私有”。然而,您相信其他人都会表现得负责任并尊重这一点,除非他们有令人信服的理由不这样做(例如,调试器和代码完成)。

如果您确实必须拥有私有的东西,那么可以在扩展中实现它(例如,在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}

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

专用函数示例

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

重要说明:

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

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

这并不意味着您可以保护您的私有数据,因为它可以通过更改变量的名称轻松访问。

资料来源:

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

实例

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

摘自《潜入Python》,3.9。私人功能:

严格来说,私人方法是在他们的班级之外不易接近。中没有任何内容Python是真正的私有;在内部,私有方法的名称和属性已损坏和未损坏让他们看起来他们的名字无法访问。你可以访问的__parse方法MP3FileInfo类(按名称)_MP3FileInfo__parse。承认这很有趣,然后承诺永远不要,永远不要用真实的代码来做。私有方法对于原因,但像许多其他事情一样Python,他们的隐私是最终是惯例问题,而不是武力

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

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

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

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