1.25 50% 的人不知道的Python 包与模块的知识盲区

1. 使用 __all__ 控制可被导入的变量

使用 from module import * 默认情况下会导入 module 里的所有变量,若你只想从模块中导入其中几个变量,可以在 module 中使用 __all__ 来控制想要被其他模块导入的变量。

# profile.py
name='wangbm'
age=27
gender='male'

__all__=['name']

打开 python console 验证一下

>>> from profile import *
>>> print(name)
wangbm
>>> print(age)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'age' is not defined
>>> print(gender)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'gender' is not defined

__all__ 仅对于使用from module import * 这种情况适用。

它经常在一个包的 __init__.py 中出现。

2. 命名空间包的神奇之处

命名空间包,一个陌生的名字。

与我们熟悉的常规包不同的是,它没有 __init__.py 文件。

更为特殊的是,它可以跨空间地将两个不相邻的子包,合并成一个虚拟机的包,我们将其称之为 命名空间包

例如,一个项目的部分代码布局如下

foo-package/
    spam/
        blah.py

bar-package/
    spam/
        grok.py

在这2个目录里,都有着共同的命名空间spam。在任何一个目录里都没有__init__.py文件。

让我们看看,如果将foo-package和bar-package都加到python模块路径并尝试导入会发生什么?

>>> import sys
>>> sys.path.extend(['foo-package', 'bar-package'])
>>> import spam.blah
>>> import spam.grok
>>>

当一个包为命名空间包时,他就不再和常规包一样具有 __file_ 属性,取而代之的是 __path__

>>> import sys
>>> sys.path.extend(['foo-package', 'bar-package'])
>>> import spam.blah
>>> import spam.grok
>>> spam.__path__
_NamespacePath(['foo-package/spam', 'bar-package/spam'])
>>> spam.__file__
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__file__'

3. 包重载就是一个坑

第一种方法

>>> import spam
>>> import imp
>>> imp.reload(spam)
<module 'spam' from './spam.py'>
>>>

由于这种重载方法,只对 import module 有效,而使用 from module import arg 导入的 arg 并不会刷新。

因此,在生产环境中可能需要避免重新加载模块。而在调试模式中,它会提供一定的便利,但你要知道这个重载的弊端,以免掉入坑里。