在Python中定义私有模块函数

335 投票
11 回答
357592 浏览
提问于 2025-04-15 14:58

根据 这个链接

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

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

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

#a.py
__num=1

还有:

#b.py
import a
print a.__num

当我运行 b.py 时,它会输出 1,而且没有任何异常。这是因为 diveintopython 的说法错了吗,还是我理解错了什么?有没有办法确实将一个模块的函数定义为私有的呢?

11 个回答

104

这个问题没有得到完全的解答,因为模块的隐私并不是完全按照约定来处理的,而且使用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
...
377

在这里,大家可能会对类私有变量模块私有变量感到困惑。

模块私有变量是以一个下划线开头的。
当你使用from <模块名> import *这种方式导入时,这种变量不会被复制;但如果你用import <模块名>的方式导入,它还是会被导入的(可以参考Ben Wilhelm的回答)。

简单来说,如果你把问题中提到的a.__num的一个下划线去掉,那么在使用from a import *导入a.py的模块中,这个变量就不会显示了。

类私有变量是以两个下划线开头的(也叫做双下划线,英文是 d-ouble under-score)。

这样的变量名字会被“变形”,也就是说它的名字会包含类名等信息。

虽然可以通过变形后的名字在类的外部访问这个变量,但这并不是推荐的做法。

虽然名字变形可以稍微防止未经授权的访问,但它的主要目的是为了避免与父类的成员变量发生名字冲突。你可以看看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
>>>
476

在Python中,“隐私”其实是看大家的共识和同意程度的——你不能强迫别人遵守。一个前面加一个下划线的变量,意味着你不应该“从外部”去访问它;而前面加两个下划线(没有后面的下划线)则更加强调这一点……不过,最终还是得靠大家的社会习惯和共识来决定:Python的反射功能很强大,你无法让全世界的程序员都遵循你的意愿。

(顺便说一下,虽然这是个小秘密,但C++也是类似的:在大多数编译器中,只需在包含你的头文件之前加一行 #define private public,聪明的程序员就能轻松破解你的“隐私”……!)

撰写回答