在python中定义私有模块函数


238

根据http://www.faqs.org/docs/diveintopython/fileinfo_private.html

像大多数语言一样,Python具有私有元素的概念:

  • 私有函数,不能从其模块外部调用

但是,如果我定义两个文件:

#a.py
__num=1

和:

#b.py
import a
print a.__num

当我运行b.py它打印出1没有给出任何例外。diveintopython是错误的,还是我误会了某些东西?而且是有一些方法可以定义模块的功能为私有?


不是说diveintopython是错误的,而是在他们的示例中: >>> import fileinfo >>> m = fileinfo.MP3FileInfo() >>> m.__parse("/music/_singles/kairo.mp3") 1 Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse' fileinfo.MP3FileInfo()是类的实例。使用双下划线时会出现此异常。在您的情况下,您没有创建类,而是仅创建了一个模块。参见:stackoverflow.com/questions/70528/...
奥梅罗Esmeraldo

Answers:


323

在Python中,“隐私”取决于“同意成年人”的协议级别-您不能强制执行它(比现实生活中的要多;-)。单个前导下划线表示您不应该 “从外部”访问它- 两个前导下划线(不带尾随下划线)可以更加有力地传达信息……但最终,它仍然取决于社交网络会议达成共识:Python的自省是有力的,以至于你无法手铐在世界上其他程序员尊重您的意愿。

((顺便说一句,尽管这是一个秘密的秘密,但对于C ++却是如此:在大多数编译器中,狡猾的编码人员只需花#define private public一行简单#include.h代码就可以对您的“隐私”进行散列...!-) )


82
您在C ++上的注释不正确。通过使用#define private public,您可以更改发送到编译器的代码,这是名称处理的地方。
rhinoinrepose 2011年

14
同样,C ++的修饰是晦涩的,但几乎不是秘密。您也可以“理解” C ++生成的二进制文件。OT,对不起。
法尔肯教授2012年

47
作为@rhinoinrepose的更新,它不仅不正确,而且根据标准用重新定义宏的关键字重新定义关键字也是不确定的行为
Cory Kramer

3
@AlexMartelli并不是static void foo()那么私密。它至少对链接器是隐藏的,并且可以通过内联完全删除该功能。
user877329

3
在现实生活中,如果人们触犯法律,就会受到起诉
LayGonzález,

288

类私有模块私有之间可能会有混淆。

模块私人与启动一个下划线
这样的元件不使用时沿复制from <module_name> import *导入命令的形式; 但是,如果使用import <moudule_name>语法将其导入(请参阅Ben Wilhelm的答案),
只需从问题示例的a .__ num中删除一个下划线,并且不会在使用该from a import *语法导入a.py的模块中显示该下划线。

类私有与开始两个下划线 (又名dunder即d-ouble下分数)
这样的变量有其名“错位”,以包括类名等
它仍然可以访问的类逻辑的外面,通过重整名称。
尽管名称改编可以用作防止未经授权访问的温和预防工具,但其主要目的是防止与祖先类的类成员发生可能的名称冲突。参见亚历克斯·马特利(Alex Martelli)有趣而准确地提及成年人的同意书,因为他描述了有关这些变量的约定。

>>> class Foo(object):
...    __bar = 99
...    def PrintBar(self):
...        print(self.__bar)
...
>>> myFoo = Foo()
>>> myFoo.__bar  #direct attempt no go
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute '__bar'
>>> myFoo.PrintBar()  # the class itself of course can access it
99
>>> dir(Foo)    # yet can see it
['PrintBar', '_Foo__bar', '__class__', '__delattr__', '__dict__', '__doc__', '__
format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__
', '__subclasshook__', '__weakref__']
>>> myFoo._Foo__bar  #and get to it by its mangled name !  (but I shouldn't!!!)
99
>>>

好,TIL。他们为何不强制执行模块级的任何原因__private_function?我遇到了这个,因此陷入了错误。
圣诞老人

感谢@Terrabits的客气话,但我很高兴在所有“ Python”方面都排名第二(仅次于Alex)。此外,考虑到Alex对语言和社区的广泛贡献,他的回答通常更加简洁幽默,同时保留了较高的权威性。
mjv

1
@mjv这是一个很有帮助的解释!谢谢!一段时间以来,我一直对这种行为感到困惑。我确实希望选择如果您尝试直接访问private类,则抛出AttributeError之外的其他类型的错误;也许是“ PrivateAccessError”或其他更明显/有用的东西。(由于出现错误,因为它没有属性并不是真正的错误)。
HFBrowning

82

由于模块隐私不是纯粹的常规,并且由于使用import可能会或可能不会识别模块隐私,这取决于使用方式,因此未完全回答此问题。

如果您在模块中定义专用名称,则这些名称被导入到使用“ import module_name”语法的任何脚本中。因此,假设您已在示例中正确定义了a.py中的私有_num模块,如下所示。

#a.py
_num=1

..您将可以在b.py中使用模块名称符号访问它:

#b.py
import a
...
foo = a._num # 1

要仅从a.py导入非特权,必须使用from语法:

#b.py
from a import *
...
foo = _num # throws NameError: name '_num' is not defined

但是,为了清楚起见,从模块导入名称时最好是显式的,而不是用'*'导入所有名称:

#b.py
from a import name1 
from a import name2
...

1
您在哪里指定要导入的功能/库?在init .py中?
FistOfFury

_names调用时不存在名称冲突的风险import a-与a._names使用此样式时一样,它们是访问权限。
Josiah Yoder

@FistOfFury是的,您指定在__init__.py文件中导入的功能。有关方面的帮助,请参见此处
Mike Williamson

29

Python允许带有双下划线前缀的私有成员。该技术在模块级别上不起作用,因此我认为这是Dive Into Python中的错误。

这是私有类函数的示例:

class foo():
    def bar(self): pass
    def __bar(self): pass

f = foo()
f.bar()   # this call succeeds
f.__bar() # this call fails

2
我认为OP的目的是编写在商业软件包之外无法访问的功能。在这方面,这个答案并不完整。仍然可以通过f._foo__bar()从外部访问__bar()函数。因此,双引号的下划线不会使其私有化。
SevakPrime,2015年

24

您可以添加一个内部函数:

def public(self, args):
   def private(self.root, data):
       if (self.root != None):
          pass #do something with data

如果您确实需要该级别的隐私,则应采用类似的方法。


9
为什么这不是最佳答案?
safay


1

嵌入闭包或函数是一种方法。这在JS中很常见,但非浏览器平台或浏览器工作程序则不需要。

在Python中,这似乎有些奇怪,但是如果确实需要隐藏某些东西,那可能就是这样。更重要的是,使用python API并保留需要隐藏在C(或其他语言)中的内容可能是最好的方法。如果没有,我会将代码放入函数中,调用该函数并使它返回要导出的项目。


-11

Python具有三种模式,分别是private,public和protected。在导入模块时,只能访问public模式。因此,不能从模块外部(即在导入时)调用private和protected模块。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.