78

如何在 Python 中将方法和数据成员设为私有?还是 Python 不支持私有成员?

4

9 回答 9

95

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.

So, for example,

class Test:
    def __private_symbol(self):
        pass
    def normal_symbol(self):
        pass

print dir(Test)

will output:

['_Test__private_symbol', 
'__doc__', 
'__module__', 
'normal_symbol']

__private_symbol should be considered a private method, but it would still be accessible through _Test__private_symbol.

于 2010-01-14T13:08:13.133 回答
37

其他答案提供了技术细节。我想强调一方面 Python 与 C++/Java 等语言之间的哲学差异(根据您的问题,我认为您对此很熟悉)。

Python(以及 Perl)中的一般态度是,属性的​​“隐私”是对程序员的请求,而不是编译器/解释器的铁丝网围栏。这个想法在这封邮件中得到了很好的总结,并且通常被称为“我们都是同意的成年人”,因为它“假设”程序员有足够的责任感,不会干涉内部。前导下划线用作礼貌信息,表示该属性是内部的。

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

于 2010-01-14T13:39:09.420 回答
12

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

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

  • single_trailing_underscore_:按惯例使用,以避免与 Python 关键字冲突,例如Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore:命名类属性时,调用名称修改(在类 FooBar 中,__boo 变为 _FooBar__boo;见下文)。

于 2010-01-14T13:41:07.030 回答
7

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).

Dive Into Python

于 2010-01-14T13:07:57.670 回答
6

Python 不直接支持隐私。程序员需要知道什么时候从外部修改属性是安全的,但无论如何使用 python,你可以通过一些小技巧来实现类似私有的东西。现在让我们看看一个人是否可以将任何内容设为私有。

class Person(object):

    def __priva(self):
        print "I am Private"
   
    def publ(self):
        print " I am public"
   
    def callpriva(self):
        self.__priva()

现在我们何时执行:

>>> p = 人()
>>> p.publ()
 我是公开的
>>> p.__priva()
回溯(最近一次通话最后):
  文件“”,第 1 行,在
    p.__priva()
AttributeError:“Person”对象没有属性“__priva”
​#Explanation :您可以在这里看到我们无法直接获取该私有方法。
>>> p.callpriva()
我是私人的
#​解释:这里我们可以访问类内部的私有方法​

​那么有人如何访问该变量???
你可以这样做:

>>> p._Person__priva
我是私人的

哇,实际上,如果 python 正在获取任何以双下划线开头的变量,则通过在开头添加单个下划线和类名来“翻译”:

注意:如果您不希望更改此名称,但仍想发送信号让其他对象远离,您可以使用单个初始下划线名称,初始下划线不使用星号导入(来自模块导入 *)
例子 :

#test.py
def hello():
    print "hello"
def _hello():
    print "Hello private"

#----------------------
#test2.py
from test import *
print hello()
print _hello()

输出-->

你好
回溯(最近一次通话最后):
  文件“”,第 1 行,在
NameError:名称'_hello'未定义

现在,如果我们手动调用 _hello 。

#test2.py
from test import _hello , hello
print hello()
print _hello()

输出-->

你好
你好私人

最后:Python 并没有真正提供同等的隐私支持,尽管单下划线和双下划线在某种程度上确实为您提供了两个级别的隐私

于 2013-12-27T05:51:38.000 回答
3

这可能有效:

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()
于 2017-02-03T19:44:43.583 回答
2

这是一个很长的答案,但我认为它解决了这里真正问题的根源——可见性范围。当我艰难地完成这个时,就挂在那里!

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

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

模块规范描述了所有打算对客户端开发人员可见的特性。在与大型项目和软件项目团队打交道时,模块的实际实现应该始终对使用它的人隐藏——它是一个与外部世界有接口的黑匣子。对于 OOD 纯粹主义者,我相信技术术语是“脱钩”和“连贯性”。模块用户只需要知道接口方法即可,无需承担实现细节的负担。

如果不首先更改其底层规范文档,则绝不应更改模块,这可能需要在更改代码之前在某些组织中进行审查/批准。

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

然后我开始编写各种类和方法,但没有函数体——只是像“print()”这样的空打印语句——足以让模块在没有语法错误的情况下编译。当这一步完成时,我编译完成的空模块——这是我的规范。如果我在一个项目团队中工作,我会在继续充实主体之前展示这个规范/界面以供审查和评论。

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

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

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

于 2017-02-12T02:34:17.550 回答
-1
import inspect


class Number:
    def __init__(self, value):
        self.my_private = value

    def set_private(self, value):
        self.my_private = value

    def __setattr__(self, my_private, value):
        f = inspect.stack()[1][3]
        if f not in ['__init__', 'set_private']:
            raise Exception("can't access private member-my_private")
        # the default behavior
        self.__dict__[my_private] = value


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


if __name__ == '__main__': 
    main()
于 2015-10-21T13:50:46.877 回答
-2

我使用 Python 2.7 和 3.5。我写了这段代码:

class MyOBject(object):
    def __init__(self):
        self.__private_field = 10


my_object = MyOBject()
print(my_object.__private_field)

运行它并得到:

AttributeError:“MyOBject”对象没有属性“__private_field”

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

于 2019-05-22T19:42:54.667 回答