Scott's Blog

学则不固, 知则不惑

0%

Python 模块与包

这篇文章会介绍 Python 中的模块和包。

Python - 模块与包

引申出模块化编程的概念,即将代码分解成小模块,各自解决不同的问题,这样使得代码更简单 (Simplicity),容易维护 (Maintainability),也更容易分享、重用代码 (Reusability)。

Python 中的模块主要有三种:

  1. 用Python写的模块
  2. 用C 写的模块
  3. 内置的模块如 itertools 模块

这篇文章只关注 Python 写的模块。

模块的搜索路径

假设你写了个模块 mod.py , 怎么使用呢?

如果你当前工作目录在合适的位置,你可以执行 import mod, 然后你便可以引用其中的对象,那什么叫合适的位置?

  1. mod.py 同一位置
  2. 你的模块位置被包含在 PYTHONPATH
  3. 模块位置在安装 Python 时标准链接库目录是定义过
  4. 你创建了一个 .pth 文件告诉 Python 去哪里找,此模块位于其中

上面四种情况,只有 PYTHONPATH 环境变量和路径文件可以被用户配置。

Python 会自动去寻找这些地方是否有你需要的包,你可以通过 sys.path 查看它的搜索顺序。

所以还有一种方法导入包,即将你的包地址放入 sys.path,它是一个列表,所以你可以执行:

1
2
sys.path.append(r'C:\Users\john')
import mod

当模块被导入后,你也可以通过 mod.__file__ 查看包的地址。

1
2
3
4
5
6
7
>>> import mod
>>> mod.__file__
'C:\\Users\\john\\mod.py'

>>> import re
>>> re.__file__
'C:\\Python36\\lib\\re.py'

导入模块

模块的存在是为了被导入 import 以使用, 就像钢琴的存在是被人演奏以产生音乐。

import module_name

import 上面已有例子,需注意 import 不能让你直接使用所有模块内的内容。模块有其私有符号表 (private symbol table), 模块以此来确定各自之间的边界。

举个例子:

1
2
3
import zoo
print(zoo.dog) # yes
print(dog) # no, dog 位于 zoo 中

from module_name import name

另一种导入模块的方式是直接导入模块内的对象:

1
2
3
from <module_name> import <name(s)>
from zoo import dog, pig # 只需要特定对象
from zoo import * # 需要所有对象

需要小心 from <module_name> import * 这种方式,除非你很清楚自己在做什么。

from module_name import name as alt_name

这种方式和上面的一样,只是多了 as 关键字以定义别名。

导入提示

导入模块的语句通常写在文件首部,但也可以写在函数中,不过 Python3 不允许在函数中 import *

防止模块导入失败,可以使用 try...except... 语句捕捉 ImportError错误。

dir() 函数可以返回当前命名空间中所有的变量。

导入模块的时候,模块的代码将会执行,如果希望模块内的某些代码只是在你需要的时候才调用执行,可以加入以下代码:

1
2
if (__name__ == '__main__'):
pass # do you work

导入模块的操作只会执行一次,考虑以下代码:

1
2
3
4
5
6
7
>>> import mod
a = [100, 200, 300]
>>> import mod
>>> import mod

>>> mod.a
[100, 200, 300]

发现第二次和第三次导入并未输出,如果想要每次导入操作都重新导入,可利用 importlib 实现。

1
2
3
4
5
6
7
8
9
>>> import mod
a = [100, 200, 300]

>>> import mod

>>> import importlib
>>> importlib.reload(mod)
a = [100, 200, 300]
<module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>

模块可以被当作脚本执行,只需要在模块所在文件夹增加一个 __main__.py 文件。

Python 包

包是一组或多组模块,包可以让你通过包的名字简单的访问其下面的各种模块。

下面是一个包的结构,其中有两个模块。

1
2
3
4
C:.
└─pkg
mod1.py
mod2.py
这时候如果你在 pkg 上级目录执行 import pkg 该包里将不会包括任何东西,无法引用 mod1 也无法引用 mod2 中的内容。

__init__.py

如果在 pkg 目录下新增 __init__.py 文件,则可以引用 __init__中的的对象。

1
2
3
4
5
C:.
└─pkg
mod1.py
mod2.py
__init__.py

在内部 mod1 模块中,你也可以访问__init__ 中的对象。

如果想在外部通过 import pkg 的方式,通过 pkg 访问 mod1mod2 中的对象,则需要在 __init__ 中导入 mod1mod2

1
2
# In __init__.py
import pkg.mod1, pkg.mod2
1
2
3
4
5
6
# 访问 Mod1 和 Mod2
import pkg

pkg.mod1.foo()

pkg.mod2.bar()

在 Python3.3 之后,Implicit Namespace Packages 发布了,定义包也可以不新建 __init__ 了。

__all__

你肯定写过 from <package_name> import * 这样的代码,其中 * 代表的是 import 什么?

如果在包的 __init__.py 中定义 __all__ 来进行控制。

pkg/__init__.py

1
2
3
4
5
6
__all__ = [
'mod1',
'mod2',
'mod3',
'mod4'
]

则所有其中的内容都会在写 from pkg import * 的时候,自动导入。

在模块中定义 __all__ 亦是。

pkg/mod1.py

1
2
3
4
5
6
7
8
__all__ = ['foo']
# 当使用 `from pkg.mod1 import *` 的时候,只有 foo 会被导入

def foo():
print('[mod1] foo()')

class Foo:
pass

区别是:

  • 对于一个包,如果__all__ 没有定义,import * 不会导入任何对象
  • 对于一个模块,如果__all__ 没有定义,import * 默认导入所有对象

Python 子包

包中可以放子包。

1
2
3
4
5
6
7
8
9
10
11
C:.
└─pkg
│ __init__.py

├─sub_pkg1
│ mod1.py
│ mod2.py

├─sub_pkg2
│ mod3.py
│ mod4.py

使用方式则需要一层一层使用 . 语法下钻:

1
2
3
4
5
6
7
8
9
10
11
import pkg.sub_pkg1.mod1
pkg.sub_pkg1.mod1.foo()

from pkg.sub_pkg1 import mod2
mod2.bar()

from pkg.sub_pkg2.mod3 import baz
baz()

from pkg.sub_pkg2.mod4 import qux as grault
grault()

如果是兄弟模块之间要导入模块怎么办?有两种方式:

全路径:

1
2
3
4
5
6
7
8
def baz():
print('[mod3] baz()')

class Baz:
pass

from pkg.sub_pkg1.mod1 import foo
foo()

相对路径:

1
2
3
4
5
6
7
8
9
10
11
def baz():
print('[mod3] baz()')

class Baz:
pass

from .. import sub_pkg1
print(sub_pkg1)

from ..sub_pkg1.mod1 import foo
foo()

参考