Python 模块
最后修改于 2024 年 1 月 29 日
本文介绍如何在 Python 中使用模块。通过几个示例展示了如何创建和使用 Python 模块。
模块是包含 Python 代码的文件。Python 模块的文件扩展名为 .py。
Python 代码可以通过以下方式进行管理:
- 函数
- 类
- 模块
- 包
Python 模块用于组织 Python 代码。例如,与数据库相关的代码放在数据库模块中,安全代码放在安全模块中,依此类推。较小的 Python 脚本可以有一个模块。但较大的程序会拆分成几个模块。模块被分组在一起形成包。
.pyc 文件
Python 会将模块的编译内容缓存到 .pyc 文件中,以加快加载模块的速度。Python 将程序源代码编译成字节码。为了提高性能,它会在源代码文件发生更改时将字节码缓存到文件系统中。
这种缓存机制可以绕过编译阶段,从而大大加快 Python 模块的加载速度。Python 将每个模块的编译版本缓存到 __pycache__ 目录中,文件名为 module.version.pyc。
Python 会检查源文件与编译版本之间的修改日期,以确定是否过时需要重新编译。
#!/usr/bin/python
import compileall
compileall.compile_dir('lib/', force=True)
compileall 模块可用于以编程方式编译 Python 模块。
Python 模块名称
模块名称是文件名加上 .py 扩展名。当我们有一个名为 empty.py 的文件时,empty 就是模块名。__name__ 是一个变量,它保存着被引用的模块的名称。当前模块,即正在执行的模块(也称为主模块)有一个特殊名称:'__main__'。通过这个名称,它可以在 Python 代码中被引用。
在当前工作目录中有两个文件:empty.py 和 test_empty.py。第二个模块是正在执行的主模块。它导入了第一个模块。模块使用 import 关键字进行导入。
""" An empty module """
这是 empty.py 模块。
#!/usr/bin/python import empty import sys print(__name__) print(empty.__name__) print(sys.__name__)
在此代码示例中,我们导入了两个模块:内置模块 sys 和一个自定义模块 empty。我们将模块的名称打印到控制台。
$ ./test_empty.py __main__ empty sys
正在执行的模块名称始终是 '__main__'。其他模块的名称与其文件名相同。可以使用 import 关键字将模块导入到其他模块中。
Python 查找模块
当导入模块时,解释器首先查找具有该名称的内置模块。如果未找到,它会在 sys.path 变量给出的目录列表中进行搜索。sys.path 是一个字符串列表,指定了模块的搜索路径。它包括当前工作目录、PYTHONPATH 环境变量中指定的目录名称以及一些额外的依赖于安装的目录。如果找不到模块,则会引发 ImportError。
#!/usr/bin/python import sys import textwrap sp = sorted(sys.path) dnames = ', '.join(sp) print(textwrap.fill(dnames))
该脚本打印 sys.path 变量中的所有目录。
import textwrap
textwrap 模块用于方便地格式化段落。
sp = sorted(sys.path)
我们从 sys.path 变量中检索目录列表并对其进行排序。
dnames = ', '.join(sp)
我们将列表转换为一个字符串。
$ ./locating_modules.py /home/jano/.local/lib/python3.10/site-packages, /home/jano/tmp/py, /usr/lib/python3.10, /usr/lib/python3.10/lib-dynload, /usr/lib/python3/dist-packages, /usr/lib/python310.zip, /usr/local/lib/python3.10/dist-packages
Python import 关键字
import 关键字可以以多种方式使用。
from module import *
此构造会将所有 Python 定义导入到另一个模块的命名空间中。有一个例外。以划线字符 _ 开头的对象不会被导入。它们被期望仅由被导入的模块在内部使用。不推荐这种导入模块的方式。
#!/usr/bin/python from math import * print(cos(3)) print(pi)
此导入构造导入了内置 math 模块的所有定义。我们可以直接调用数学函数,而无需引用 math 模块。
$ ./everything.py -0.9899924966004454 3.141592653589793
使用此导入构造可能会导致命名空间污染。我们可能会有几个同名对象,它们的定义可能会被覆盖。
#!/usr/bin/python from math import * pi = 3.14 print(cos(3)) print(pi)
示例将在控制台打印 3.14。这可能不是我们想要的。在较大的项目中,命名空间污染可能会变得很关键。
未导入的 Python 对象
下面的示例显示了使用此 import 构造不会导入的定义。
#!/usr/bin/python
"""
names is a test module
"""
_version = 1.0
names = ["Paul", "Frank", "Jessica", "Thomas", "Katherine"]
def show_names():
for i in names:
print(i)
def _show_version():
print(_version)
#!/usr/bin/python from names import * print(locals()) show_names()
_version 变量和 _show_version 函数未导入到 test_names 模块中。我们在命名空间中看不到它们。locals 函数为我们提供了模块中所有可用的定义。
导入特定对象
使用 from 和 import 关键字,可以仅导入某些对象。
from module import fun, var
此导入构造仅从模块导入特定对象。这样我们只导入我们需要的定义。
#!/usr/bin/python from math import sin, pi print(sin(3)) print(pi)
我们从 math 模块导入了两个对象。我们无法引用余弦函数等其他定义。
#!/usr/bin/python from names import _version, _show_version print(_version) _show_version()
我们也可以导入以划线开头的定义。但这是一种不良的做法。
$ ./imnames.py 1.0 1.0
Python import 模块
最后一种构造最为常用。
import module
它避免了命名空间污染,并允许访问模块中的所有定义。
#!/usr/bin/python import math pi = 3.14 print(math.cos(3)) print(math.pi) print(math.sin(3)) print(pi)
在这种情况下,我们通过模块名称引用定义。正如我们所见,我们可以同时使用 pi 变量,包括我们自己的定义和来自 math 模块的定义。
$ ./impmod.py -0.9899924966004454 3.141592653589793 0.1411200080598672 3.14
Python 模块别名
我们可以使用 as 关键字为模块创建别名。
#!/usr/bin/python # importas.py import math as m print(m.pi) print(m.cos(3))
我们可以更改引用模块的名称。为此,我们使用 as 关键字。
$ ./importas.py 3.14159265359 -0.9899924966
ImportError
如果无法导入模块,则会引发 ImportError。
#!/usr/bin/python
try:
import empty2
except ImportError as e:
print('Failed to import:', e)
我们没有创建 empty2 模块。因此会引发异常。
$ ./importerror.py Failed to import: No module named empty2
执行 Python 模块
模块可以导入到其他模块中,也可以被执行。模块作者通常会创建一个测试套件来测试模块。只有当模块作为脚本执行时,__name__ 属性才等于 '__main__'。
我们将以斐波那契模块为例来演示这一点。斐波那契数是一个数字序列,其中每个数字都是其前面两个数字之和。
#!/usr/bin/python
"""
A module containing the fibonacci
function.
"""
def fib(n):
a, b = 0, 1
while b < n:
print(b, end=" ")
(a, b) = (b, a + b)
# testing
if __name__ == '__main__':
fib(500)
模块可以像往常一样正常导入。也可以执行模块。
$ ./fibonacci.py 1 1 2 3 5 8 13 21 34 55 89 144 233 377
如果我们导入了斐波那契模块,测试不会自动执行。
>>> import fibonacci as fib >>> fib.fib(500) 1 1 2 3 5 8 13 21 34 55 89 144 233 377
斐波那契模块已导入,并且 fib 函数已执行。
Python dir 函数
内置的 dir 函数返回一个排序的字符串列表,其中包含模块定义的名称。
#!/usr/bin/python
"""
This is dirfun module
"""
import math, sys
version = 1.0
names = ["Paul", "Frank", "Jessica", "Thomas", "Katherine"]
def show_names():
for i in names:
print(i)
print(dir())
在此模块中,我们导入了两个系统模块。我们定义了一个变量、一个列表和一个函数。
print(dir())
dir 函数返回模块当前命名空间中所有可用的名称。
$ ./dirfun.py ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'math', 'names', 'show_names', 'sys', 'version']
我们可以看到一些内置名称,如 '__file__' 或 '__name__',以及我们定义和导入的所有其他名称。
Python globals 函数
globals 函数返回一个字典,表示当前的全局命名空间。它是一个全局名称及其值的字典。它是当前模块的字典。
#!/usr/bin/python
import textwrap
version = 1.0
def myfun():
pass
gl = globals()
gnames = ', '.join(gl)
print(textwrap.fill(gnames))
我们使用 globals 函数打印当前模块的所有全局名称。
$ ./globalsfun.py textwrap, __package__, version, __builtins__, __name__, __spec__, __doc__, gl, __cached__, myfun, __loader__, __file__
这些是当前模块的全局名称。
Python __module__ 属性
__module__ 类属性保存了类定义的模块的名称。
"""
module animals
"""
class Cat:
pass
class Dog:
pass
这是 animals.py 文件的内容。我们有两个类。
#!/usr/bin/python
from animals import Cat
class Being:
pass
b = Being()
print(b.__module__)
c = Cat()
print(c.__module__)
在此代码中,我们使用了 __module__ 属性。
from animals import Cat
从 animals 模块导入 Cat 类。
class Being:
pass
在当前模块中,我们定义了一个类 Being。
b = Being() print(b.__module__)
创建了一个 Being 类的实例。我们打印其模块的名称。
c = Cat() print(c.__module__)
我们创建了一个 Cat 类的对象。我们还打印了其定义的模块。
$ ./mclass.py __main__ animals
当前模块的名称是 '__main__'。而 Cat 的模块名称是 animals。
来源
本文介绍了 Python 模块。
作者
列出所有 Python 教程。