Python中的私有成员

时间:2010-01-14 13:06:20

标签: python class oop member-variables

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

9 个答案:

答案 0 :(得分:74)

  

9.6. Private Variables

     

“私有”实例变量   除了从里面以外不能访问   一个对象,在Python中不存在。   但是,有一个惯例   接下来是大多数Python代码:一个名字   以下划线为前缀(例如   _spam)应被视为API的非公开部分(无论是否   是一种功能,方法或数据   会员)。应该考虑一个   实施细节和主题   如有更改,恕不另行通知。

     

因为有一个有效的用例   阶级私人成员(即避免   名称与名称冲突   由子类定义),有   对这种机制的支持有限,   叫名字错误。任何标识符   形式__spam(至少两个   领先的下划线,最多一个   尾随下划线)是文本上的   替换为_classname__spam,其中   classname是当前的类名   领先的下划线被剥离。   这种破坏是不加考虑的   到了句法的位置   标识符,只要它发生   在一个类的定义内。

所以,for example

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

print dir(Test)

将输出:

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

__private_symbol应被视为私有方法,但仍可通过_Test__private_symbol访问。

答案 1 :(得分:32)

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

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

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

答案 2 :(得分:6)

  

如果是Python函数的名称,   类方法,或属性以。开头   (但不以...结尾)两个   下划线,它是私人的; 一切   否则是公开的。 Python没有概念   受保护的类方法(可访问   只有在自己的班级和后代   类)。类方法也是   私人的(只能在他们自己的   类)或公共(可从   任意位置)。

Dive Into Python

答案 3 :(得分:6)

Python中没有任何其他访问保护机制privatePython style guide中记录了一项约定,用于向您的班级用户表明他们不应该访问某些属性。

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

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

  • __ double_leading_underscore:在命名一个类属性时,调用名称修改(在类FooBar中,__ boo变为_FooBar__boo;见下文)。

答案 4 :(得分:5)

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 = 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​

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

>>>p._Person__priva
I am Private
哇,实际上如果python得到任何以双下划线开头的变量都是通过在开头添加一个下划线和类名来“翻译”的:

注意:如果您不希望此名称发生更改,但您仍希望发送其他对象的信号以避开,则可以使用带有初始下划线的单个初始下划线名称使用已加星标的导入导入(来自模块导入*)
示例:

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

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

输出 - >

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

现在我们将手动调用_hello。

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

输出 - >

hello
hello private

最后:虽然单一,但Python并没有真正的等效隐私支持 并且双初始下划线在某种程度上可以为您提供两个级别的隐私

答案 5 :(得分:2)

这可能有效:

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

答案 6 :(得分:2)

这有点像l-o-n-g的答案,但我认为它在这里找到了真正问题的根源 - 可见度范围。只是在我偷偷摸摸的时候就在那里!

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

那些通过导入模块开发基于基本类和方法的更高级抽象的文档将提供规范文档 - 而不是实际的源代码。

模块规范描述了客户端开发人员可以看到的所有功能。在处理大型项目和软件项目团队时,模块的实际实现应该总是对使用它的人保持隐藏 - 它是一个带有外部世界接口的黑盒子。对于OOD纯粹主义者,我相信技术术语是"脱钩"和"连贯性"。模块用户只需要了解接口方法,而不必担心实现细节。

不应在未事先更改其基础规范文档的情况下更改模块,在更改代码之前,可能需要在某些组织中进行审核/批准。

作为业余爱好程序员(现已退休),我启动了一个新模块,其中spec规范文件实际上是作为模块顶部的巨大注释块写出来的,这将是用户实际在规范库中看到的部分。因为它只是我,我还没有建立一个图书馆,但这很容易做到。

然后我通过编写各种类和方法开始编码,但没有函数体 - 只需要打印语句,如" print()" - 足以允许模块编译而不会出现语法错误。当这一步完成后,我编译完成的空模块 - 这是我的规范。如果我在一个项目团队工作,我会提出这个规范/界面供审查和在进行充实身体之前的评论。

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

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

PS:早在开始之前,我就在国防航空航天界工作,我们做了一些非常酷的事情,但是专有算法和敏感系统控制逻辑之类的东西在超级安全软件库中被严格保存和加密。我们可以访问模块/包接口,但不能访问blackbox实现主体。有一个文档管理工具可以处理所有系统级设计,软件规范,源代码和测试记录 - 它们都是同步的。政府对软件质量保证标准有严格的要求。任何人都记得一种名为" Ada&#34 ;?的语言那我多大了!

答案 7 :(得分:0)

我使用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

答案 8 :(得分:-1)

如果你想在Python中将方法或数据成员设为私有, 使用__setattr __

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