Python中的私人成员
我如何使方法和数据成员在Python中是私有的? 或者Python不支持私有成员?
9.6。 私有variables
除了在对象内部不能被访问的“私有”实例variables,在Python中不存在。 但是,大多数Python代码都有一个约定:以下划线(例如_spam)作为前缀的名称应被视为API的非公开部分(无论是函数,方法还是数据成员) 。 这应该被视为实施细节,如有更改,恕不另行通知。
由于类私有成员有一个有效的用例(即为了避免名称与由子类定义的名称发生冲突),对这种称为名称修改的机制的支持是有限的。 任何forms的__spam标识符(至less两个前导下划线,最多一个尾随下划线)被文本replace为
_classname__spam
,其中classname是当前类名,前导下划线被去除。 只要发生在类的定义内,就不必考虑标识符的语法位置。
所以, 例如 ,
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
访问。
其他答案提供了技术细节。 我想强调Python与Python之间的哲学差异,如C ++ / Java(我认为你根据你的问题熟悉)。
Python(以及Perl)的一般态度是属性的“隐私”是编程人员对编程人员的请求,而不是编译器/解释器的铁丝网围栏。 这个想法在这个邮件里总结得很好,通常被称为“我们都同意成年人”,因为它假设程序员有责任不去干涉内部。 主要的下划线作为礼貌的信息,说属性是内在的。
另一方面,如果您确实想要访问某些应用程序的内部(一个值得注意的例子是像pydoc这样的文档生成器),您可以自由地这样做。 Onus对你来说是一个程序员,知道你在做什么,而不是用语言去强迫你做事。
如果Python函数,类方法或属性的名称以两个下划线开始(但不以两个下划线结尾),则它是私有的; 其他一切都是公开的。 Python没有受保护的类方法的概念(只能在自己的类和后代类中访问)。 类方法是私有的(只能在自己的类中访问)或者公共的(从任何地方访问)。
深入Python
Python不直接支持隐私。 程序员需要知道什么时候可以从外部修改属性是安全的,但无论如何,用python,你可以实现私人的小窍门。 现在让我们看看一个人可以把任何东西都隐藏起来。
类Person(object): def __priva(self): 打印“我是私人的” def publ(self): 打印“我是公众” def callpriva(self): 自.__普里瓦()
现在当我们执行:
>>> p = Person() >>> p.publ() 我是公众 >>> p .__私有() 回溯(最近一次通话最后): 文件“”,第1行, p .__普里瓦() AttributeError:'Person'对象没有属性'__priva' #说明:您可以在这里看到我们无法直接获取该私有方法。 >>> p.callpriva() 我是私人的 #说明:这里我们可以访问类中的私有方法
那么如何有人可以访问该variables?
你可以这样做:
>>> p._Person__priva 我是私人的
哇,实际上,如果python获取任何以双下划线开始的variables是通过添加一个下划线和类名到开头“翻译”:
注意:如果你不想改变这个名称,但是你仍然想发送一个信号让其他对象远离,那么你可以使用一个初始的下划线名字,而不是用星号导入(从模块导入*)导入。
例如:
#test.py def hello(): 打印“你好” def _hello(): 打印“你好私人” #----------------------
#test2.py 从testing导入* 打印hello() 打印_hello()
输出 – >
你好 回溯(最近一次通话最后): 文件“”,第1行, NameError:名称'_hello'未定义
现在,如果我们将手动调用_hello。
#test2.py 从testing导入_hello,你好 打印hello() 打印_hello()
输出 – >
你好 你好私人
最后一点:Python并没有真正的隐私支持,尽pipe单个和双重的首字母下划线在一定程度上可以为您提供两个级别的隐私
Python中没有任何其他的访问保护机制。 在Python风格指南中有一个约定,用来向你的类的用户表明他们不应该访问某个属性。
-
_single_leading_underscore:弱的“内部使用”指标。 例如
from M import *
不导入名称以下划线开头的对象。 -
single_trailing_underscore_:按惯例使用,以避免与Python关键字冲突,例如
Tkinter.Toplevel(master, class_='ClassName')
-
__double_leading_underscore:当命名一个类属性时,调用名字改变(在类FooBar中,__boo变成_FooBar__boo;见下文)。
这可能工作:
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()
这是一个很长的答案,但是我认为这是真正的问题的根源 – 可见度的范围。 当我通过这个闯入时,就挂在那里!
简单地导入一个模块并不一定能让应用程序开发人员访问所有的类或方法; 如果我实际上看不到模块源代码,我怎么知道有什么可用的? 有些人(或者某些人)不得不告诉我我能做些什么,并解释如何使用这些function,否则整个事情对我来说是没用的。
那些基于基本类和方法通过导入模块开发更高层次的抽象的是用规范DOCUMENT – 而不是实际的源代码。
模块规范描述了客户端开发人员可以看到的所有function。 在处理大型项目和软件项目团队的时候,模块的实际执行应该总是隐藏在使用它的人之间 – 这是一个带有与外界交互的黑盒子。 对于OOD纯粹主义者,我认为技术术语是“脱钩”和“连贯”。 模块用户只需要知道接口方法,而不用担心执行的细节。
如果不改变基本的规范文档,就不能改变模块,在更改代码之前可能需要在某些组织中进行审查/批准。
作为业余爱好程序员(现在已经退休),我开始了一个新的模块,其中spec doc实际上是作为模块顶部的一个巨大的注释块写出来的,这将是用户在spec库中看到的部分。 既然只是我,我还没有build立一个图书馆,但是这样做很容易。
然后,我开始通过编写各种类和方法来编写代码,但是没有函数体 – 只是像print()这样的null打印语句 – 这足以让模块在没有语法错误的情况下编译。 当这一步完成后,我编译完成的空模块 – 这是我的规格。 如果我在一个项目团队工作,我会提出这个规范/界面进行审查和评论,然后进行充实的身体。
我一次一个地填充每个方法的主体,并相应地进行编译,确保即时dynamic修复语法错误。 这也是开始在底部编写一个临时的“主要”执行部分来testing每种方法的好时机。 当编码/testing完成后,所有的testing代码都会被注释掉,直到需要更新时再次需要。
在现实世界的开发团队中,规范注释块也会出现在文档控制库中,但这是另一回事。 重点是:作为模块客户端,您只能看到这个规范而不是源代码。
PS:早在开始之前,我就在国防航空领域工作,我们做了一些非常酷的事情,但是专有algorithm和敏感系统控制逻辑等东西都是在超级安全的软件库中进行了严格的保护和encryption。 我们可以访问模块/包接口,但不能访问黑盒实现主体。 有一个文档pipe理工具可以处理所有的系统级devise,软件规格,源代码和testinglogging – 它们全部同步在一起。 政府对软件质量保证标准有严格要求。 任何人都记得一个叫“阿达”的语言? 这是我多大了!
如果您想在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()