关于类:Python中的私有成员

Private members in Python

如何使方法和数据成员在Python中成为私有的?或者python不支持私有成员?


9.6. Private Variables

"Private" instance variables that
cannot be accessed except from inside
an object, don’t exist in Python.
However, there is a convention that is
followed by most Python code: a name
prefixed with an underscore (e.g.
_spam) should be treated as a non-public part of the API (whether it
is a function, a method or a data
member). It should be considered an
implementation detail and subject to
change without notice.

Since there is a valid use-case for
class-private members (namely to avoid
name clashes of names with names
defined by subclasses), there is
limited support for such a mechanism,
called name mangling. Any identifier
of the form __spam (at least two
leading underscores, at most one
trailing underscore) is textually
replaced with _classname__spam, where
classname is the current class name
with leading underscore(s) stripped.
This mangling is done without regard
to the syntactic position of the
identifier, as long as it occurs
within the definition of a class.

例如,

1
2
3
4
5
6
7
class Test:
    def __private_symbol(self):
        pass
    def normal_symbol(self):
        pass

print dir(Test)

将输出:

1
2
3
4
['_Test__private_symbol',
'__doc__',
'__module__',
'normal_symbol']

__private_symbol应该被认为是一种私有方法,但是它仍然可以通过_Test__private_symbol访问。


其他答案提供了技术细节。我想强调一方面Python和C++/Java语言之间的哲学上的差异(我假定你是基于你的问题熟悉的)。

在python(和perl)中,一般的态度是属性的"隐私"是对程序员的请求,而不是编译器/解释器的铁丝网。这个想法在这封邮件中得到了很好的总结,通常被称为"我们都是同意的成年人",因为它"假定"程序员有足够的责任不干预内部。前导下划线用作礼貌的消息,表示属性是内部的。

另一方面,如果您确实想访问某些应用程序的内部结构(一个值得注意的例子是文档生成器,如pydoc),您可以自由地这样做。作为一个程序员,你有责任知道自己在做什么,并正确地完成它,而不是依靠语言来强迫你按照它的方式去做。


python中没有任何其他访问保护机制的private。在python样式指南中有一个约定,用于向类的用户指示他们不应该访问某些属性。

  • _单_前导_下划线:弱"内部使用"指标。例如,from M import *不导入名称以下划线开头的对象。

  • single_trailing_underline_u:由约定使用,以避免与python关键字冲突,例如Tkinter.Toplevel(master, class_='ClassName')

  • _双前导下划线:在命名类属性时,调用名称管理(在类foobar内,uboo变为foobar_uboo;见下文)。

  • 小精灵


    If the name of a Python function,
    class method, or attribute starts with
    (but doesn't end with) two
    underscores, it's private; everything
    else is public. Python has no concept
    of protected class methods (accessible
    only in their own class and descendant
    classes). Class methods are either
    private (accessible only in their own
    class) or public (accessible from
    anywhere).

    潜入Python


    python不直接支持隐私。程序员需要知道何时从外部修改属性是安全的,但无论如何,使用Python,您可以通过一些小技巧实现私有化。现在让我们看看一个人是否可以把任何私人的东西放在它上面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Person(object):

        def __priva(self):
            print"I am Private"

        def publ(self):
            print" I am public"

        def callpriva(self):
            self.__priva()

    现在,当我们执行:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    >>> p = Person()
    >>> p.publ()
     I am public
    >>> p.__priva()
    Traceback (most recent call last):
      File"", line 1, in
        p.__priva()
    AttributeError: 'Person' object has no attribute '__priva'
    ?#Explanation   : You can see  here we are not able to fetch that private method directly.
    >>> p.callpriva()
    I am Private
    #?Explanation  : Here we can access private method inside class?

    ?那么,有人如何访问这个变量呢????你可以这样做:

    1
    2
    >>>p._Person__priva
    I am Private

    ?哇,实际上,如果python正在获取任何以双下划线开头的变量,那么通过在开头添加一个下划线和类名来"翻译"它们:

    注意:如果不希望更改此名称,但仍希望发送信号让其他对象离开,则可以使用带有初始下划线的单个初始下划线名称而不使用带星号的导入(从模块导入*)例子:

    1
    2
    3
    4
    5
    6
    7
    #test.py
    def hello():
        print"hello"
    def _hello():
        print"Hello private"

    #----------------------

    1
    2
    3
    4
    #test2.py
    from test import *
    print hello()
    print _hello()

    输出-->

    1
    2
    3
    4
    hello
    Traceback (most recent call last):
      File"", line 1, in
    NameError: name '_hello' is not defined

    现在,如果我们手动打电话给你好。

    1
    2
    3
    4
    #test2.py
    from test import _hello , hello
    print hello()
    print _hello()

    输出-->

    1
    2
    hello
    hello private

    最后:python实际上没有同等的隐私支持,尽管它是单一的双首下划线在某种程度上为您提供了两个级别的隐私


    这有点像是一个L-O-N-G的答案,但我认为它找到了真正问题的根源——可见性范围。在我艰难地完成这件事的时候,坚持住!

    简单地导入一个模块不一定能让应用程序开发人员访问它的所有类或方法;如果我不能真正看到模块源代码,我怎么知道什么是可用的?有人(或某些事情)必须告诉我我可以做什么,并解释如何使用那些允许我使用的功能,否则整个事情对我来说都是无用的。

    那些通过导入的模块开发基于基本类和方法的高级抽象的人会得到一个规范文档,而不是实际的源代码。

    模块规范描述了客户机开发人员可以看到的所有特性。在处理大型项目和软件项目团队时,模块的实际实现应该始终对使用它的人保持隐藏——它是一个带有外部世界接口的黑盒。对于纯粹主义者来说,我认为技术术语是"脱钩"和"连贯"。模块用户只需要知道接口方法,而不需要对实现细节进行负担。

    如果不首先更改模块的基础规范文档,则不应更改模块,因为在更改代码之前,可能需要在某些组织中进行审查/批准。

    作为业余程序员(现在退休了),我开始了一个新的模块,在模块的顶部有一个巨大的注释块,这将是用户在规范库中实际看到的部分。因为只有我一个人,我还没有建立一个图书馆,但这很容易做到。

    然后,我通过编写各种类和方法开始编码,但没有函数体——只有"print()"之类的空print语句——这足以让模块编译时不出现语法错误。完成此步骤后,我将编译完成的空模块——这是我的规范。如果我在项目团队中工作,在继续充实主体之前,我将呈现此规范/接口以供审阅和注释。

    我一次充实每个方法的主体,并相应地编译,确保立即修复语法错误。这也是开始在底部编写一个临时的"主"执行部分的好时机,以便在编写代码时测试每个方法。当编码/测试完成时,所有测试代码都会被注释掉,直到您再次需要它时才需要更新。

    在实际的开发团队中,spec注释块也会出现在文档控制库中,但这是另一个故事。要点是:作为模块客户机,您只能看到这个规范,而不能看到源代码。

    PS:很久以前,我在国防航空航天界工作,我们做了一些很酷的工作,但是像专有算法和敏感系统控制逻辑之类的东西都在超级duper安全软件库中得到了严密的保管和加密。我们可以访问模块/包接口,但不能访问blackbox实现体。有一个文档管理工具可以处理所有系统级的设计、软件规范、源代码和测试记录——所有这些都是同步的。政府对软件质量保证标准有严格要求。有人记得一种叫"Ada"的语言吗?我多大了!


    这可能有效:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import sys, functools

    def private(member):
        @functools.wraps(member)
        def wrapper(*function_args):
          myself = member.__name__
          caller = sys._getframe(1).f_code.co_name
          if (not caller in dir(function_args[0]) and not caller is myself):
             raise Exception("%s called by %s is private"%(myself,caller))
          return member(*function_args)
        return wrapper

    class test:
       def public_method(self):
          print('public method called')

       @private
       def private_method(self):
          print('private method called')

    t = test()
    t.public_method()
    t.private_method()


    我使用的是python 2.7和3.5。我写了这个代码:

    1
    2
    3
    4
    5
    6
    7
    class MyOBject(object):
        def __init__(self):
            self.__private_field = 10


    my_object = MyOBject()
    print(my_object.__private_field)

    跑了,得到:

    AttributeError: 'MyOBject' object has no attribute '__private_field'

    请参见:https://www.tutorialsteacher.com/python/private-and-protected-access-modifiers-in-python


    如果要使方法或数据成员在Python中成为私有的,使用u setattr__

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Number:
        def __init__(self,value):
            self.my_private = value

        def __setattr__(self, my_private, value):
            # the default behavior
            # self.__dict__[my_private] = value
            raise Exception("can't access private member-my_private")


    def main():
        n = Number(2)
        print(n.my_private)

    if __name__ == '__main__':
        main()