John Sturtz
Python 模块:概述
在 Python 中,实际上有三种不同的方式来定义一个模块:
- 模块可以用 Python 语言本身编写。
- 模块可以用 C 语言编写,并在运行时动态加载,例如
re(正则表达式)模块。 - 内置模块直接内嵌于解释器中,例如
itertools模块。
无论采用哪种方式定义模块,访问其内容的方式都是一致的:使用 import 语句。
本文主要关注用 Python 编写的模块。这类模块的构建极其简单:你只需创建一个包含合法 Python 代码的文件,并给它一个 .py 扩展名即可。无需任何特殊语法!
例如,假设你创建了一个名为 mod.py 的文件,内容如下:
# mod.py
s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]
def foo(arg):
print(f'arg = {arg}')
class Foo:
pass
在 mod.py 中定义了以下几个对象:
s(一个字符串)a(一个列表)foo()(一个函数)Foo(一个类)
只要 mod.py 位于合适的位置(稍后会详细介绍),就可以通过导入该模块来访问这些对象:
>>> import mod
>>> print(mod.s)
If Comrade Napoleon says it, it must be right.
>>> mod.a
[100, 200, 300]
>>> mod.foo(['quux', 'corge', 'grault'])
arg = ['quux', 'corge', 'grault']
>>> x = mod.Foo()
>>> x
<mod.Foo object at 0x03C181F0>
模块搜索路径
继续上面的例子,我们来看看当 Python 执行以下语句时会发生什么:
import mod
当解释器执行上述 import 语句时,它会在由以下来源组成的目录列表中搜索 mod.py 文件:
- 运行输入脚本所在的目录,或者如果以交互方式运行解释器,则为当前目录。
- 如果设置了
PYTHONPATH环境变量,则包含其中列出的目录(PYTHONPATH的格式依赖于操作系统,但应模仿PATH环境变量的格式)。 - Python 安装时配置的、与安装相关的目录列表。
最终生成的搜索路径可通过 Python 变量 sys.path 访问,该变量来自名为 sys 的模块:
>>> import sys
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages']
注意:
sys.path的确切内容依赖于具体安装环境。在你的计算机上,上述内容几乎肯定会略有不同。
因此,为了确保你的模块能被找到,你需要执行以下操作之一:
- 将
mod.py放在输入脚本所在的目录中,或交互模式下的当前目录中; - 在启动解释器前,修改
PYTHONPATH环境变量,使其包含mod.py所在的目录; - 或者:将
mod.py放在PYTHONPATH变量中已包含的某个目录里; - 将
mod.py放在 Python 安装时配置的某个目录中(根据操作系统不同,你可能没有写入权限)。
实际上还有一个额外选项:你可以将模块文件放在任意目录中,然后在运行时修改 sys.path,使其包含该目录。例如,在本例中,你可以将 mod.py 放在 C:\Users\john 目录下,然后执行以下语句:
>>> sys.path.append(r'C:\Users\john')
>>> sys.path
['', 'C:\\Users\\john\\Documents\\Python\\doc', 'C:\\Python36\\Lib\\idlelib',
'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib',
'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'C:\\Users\\john']
>>> import mod
一旦模块被导入,你可以通过模块的 __file__ 属性确定其所在位置:
>>> import mod
>>> mod.__file__
'C:\\Users\\john\\mod.py'
>>> import re
>>> re.__file__
'C:\\Python36\\lib\\re.py'
__file__ 的目录部分应当是 sys.path 中的某个目录。
import 语句
模块的内容通过 import 语句提供给调用者。import 语句有多种形式,如下所示。
import <module_name>
最简单的形式就是上面已经展示过的:
import <module_name>
注意,这种方式不会使模块内容直接对调用者可见。每个模块都有自己的私有符号表,作为模块中所有对象的全局符号表。因此,模块会创建一个独立的命名空间。
import <module_name> 语句仅将 <module_name> 放入调用者的符号表中。模块中定义的对象仍保留在模块的私有符号表中。
从调用者的角度看,只有通过 <module_name>. 前缀(点表示法)才能访问模块中的对象,如下所示。
执行以下导入语句后,mod 被放入本地符号表。因此,在调用者的本地上下文中,mod 是有意义的:
>>> import mod
>>> mod
<module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>
但 s 和 foo 仍保留在模块的私有符号表中,在本地上下文中没有意义:
>>> s
NameError: name 's' is not defined
>>> foo('quux')
NameError: name 'foo' is not defined
要在本地上下文中访问模块中定义的对象名称,必须加上 mod 前缀:
>>> mod.s
'If Comrade Napoleon says it, it must be right.'
>>> mod.foo('quux')
arg = quux
你也可以在一条 import 语句中指定多个以逗号分隔的模块:
import <module_name>[, <module_name> ...]
from <module_name> import <name(s)>
import 语句的另一种形式允许将模块中的单个对象直接导入到调用者的符号表中:
from <module_name> import <name(s)>
执行上述语句后,<name(s)> 可以在调用者的环境中直接引用,无需 <module_name> 前缀:
>>> from mod import s, foo
>>> s
'If Comrade Napoleon says it, it must be right.'
>>> foo('quux')
arg = quux
>>> from mod import Foo
>>> x = Foo()
>>> x
<mod.Foo object at 0x02E3AD50>
由于这种形式的 import 会将对象名称直接放入调用者的符号表中,因此任何已存在的同名对象将被覆盖:
>>> a = ['foo', 'bar', 'baz']
>>> a
['foo', 'bar', 'baz']
>>> from mod import a
>>> a
[100, 200, 300]
你甚至可以一次性不加选择地导入模块中的所有内容:
from <module_name> import *
这会将 <module_name> 中所有对象的名称(除了以下划线 _ 开头的)放入本地符号表。
例如:
>>> from mod import *
>>> s
'If Comrade Napoleon says it, it must be right.'
>>> a
[100, 200, 300]
>>> foo
<function foo at 0x03B449C0>
>>> Foo
<class 'mod.Foo'>
在大规模生产代码中,不推荐这样做。这样做有点危险,因为你正在大量地将名称引入本地符号表。除非你非常清楚所有名称且确信不会发生冲突,否则很可能无意中覆盖现有名称。然而,当你在交互式解释器中进行测试或探索时,这种语法非常方便,因为它能快速让你访问模块提供的所有内容,而无需大量输入。
from <module_name> import <name> as <alt_name>
你也可以导入单个对象,但使用替代名称将其放入本地符号表:
from <module_name> import <name> as <alt_name>[, <name> as <alt_name> …]
这样可以在避免与已有名称冲突的同时,直接将名称放入本地符号表:
>>> s = 'foo'
>>> a = ['foo', 'bar', 'baz']
>>> from mod import s as string, a as alist
>>> s
'foo'
>>> string
'If Comrade Napoleon says it, it must be right.'
>>> a
['foo', 'bar', 'baz']
>>> alist
[100, 200, 300]
import <module_name> as <alt_name>
你也可以用替代名称导入整个模块:
import <module_name> as <alt_name>
>>> import mod as my_module
>>> my_module.a
[100, 200, 300]
>>> my_module.foo('qux')
arg = qux
在函数内部导入模块
模块内容可以从函数定义内部导入。在这种情况下,导入操作直到函数被调用时才会发生:
>>> def bar():
... from mod import foo
... foo('corge')
...
>>> bar()
arg = corge
不过,Python 3 不允许在函数内部使用无限制的 import * 语法:
>>> def bar():
... from mod import *
...
SyntaxError: import * only allowed at module level
使用 try/except 处理导入失败
最后,可以使用带有 except ImportError 子句的 try 语句来防范导入失败的情况:
>>> try:
... # 不存在的模块
... import baz
... except ImportError:
... print('Module not found')
...
Module not found
>>> try:
... # 存在的模块,但不存在的对象
... from mod import baz
... except ImportError:
... print('Object not found in module')
...
Object not found in module
dir() 函数
内置函数 dir() 返回命名空间中已定义名称的列表。不带参数时,它会生成当前本地符号表中名称的字母排序列表:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> qux = [1, 2, 3, 4, 5]
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'qux']
>>> class Bar():
... pass
...
>>> x = Bar()
>>> dir()
['Bar', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'qux', 'x']
注意,第一次调用 dir() 列出了几个在解释器启动时自动定义并已在命名空间中的名称。随着新名称的定义(qux、Bar、x),它们会在后续调用 dir() 时出现。
这对于识别 import 语句到底向命名空间添加了哪些内容非常有用:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> import mod
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'mod']
>>> mod.s
'If Comrade Napoleon says it, it must be right.'
>>> mod.foo([1, 2, 3])
arg = [1, 2, 3]
>>> from mod import a, Foo
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'mod']
>>> a
[100, 200, 300]
>>> x = Foo()
>>> x
<mod.Foo object at 0x002EAD50>
>>> from mod import s as string
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'mod', 'string', 'x']
>>> string
'If Comrade Napoleon says it, it must be right.'
当给 dir() 传入一个模块名称作为参数时,它会列出该模块中定义的名称:
>>> import mod
>>> dir(mod)
['Foo', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', 'a', 'foo', 's']
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> from mod import *
>>> dir()
['Foo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'a', 'foo', 's']
将模块作为脚本执行
任何包含模块的 .py 文件本质上也是一个 Python 脚本,完全可以像脚本一样执行。
再次回顾之前定义的 mod.py:
# mod.py
s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]
def foo(arg):
print(f'arg = {arg}')
class Foo:
pass
它可以作为脚本运行:
C:\Users\john\Documents>python mod.py
C:\Users\john\Documents>
没有报错,说明运行成功了。虽然没什么意思——因为它只定义了对象,没有对它们做任何操作,也没有生成输出。
让我们修改上述 Python 模块,使其在作为脚本运行时生成一些输出:
# mod.py
s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]
def foo(arg):
print(f'arg = {arg}')
class Foo:
pass
print(s)
print(a)
foo('quux')
x = Foo()
print(x)
现在应该更有趣一些:
C:\Users\john\Documents>python mod.py
If Comrade Napoleon says it, it must be right.
[100, 200, 300]
arg = quux
<__main__.Foo object at 0x02F101D0>
不幸的是,现在它在作为模块导入时也会生成输出:
>>> import mod
If Comrade Napoleon says it, it must be right.
[100, 200, 300]
arg = quux
<mod.Foo object at 0x0169AD50>
这可能不是你想要的。通常模块在被导入时不应生成输出。
有没有办法区分文件是作为模块加载还是作为独立脚本运行呢?
当然有!
当 .py 文件作为模块导入时,Python 会将特殊的双下划线变量 __name__ 设置为模块的名称。但是,如果文件作为独立脚本运行,__name__ 会被(富有创意地)设置为字符串 '__main__'。利用这一点,你可以在运行时判断具体情况并相应地调整行为:
# mod.py
s = "If Comrade Napoleon says it, it must be right."
a = [100, 200, 300]
def foo(arg):
print(f'arg = {arg}')
class Foo:
pass
if (__name__ == '__main__'):
print('Executing as standalone script')
print(s)
print(a)
foo('quux')
x = Foo()
print(x)
现在,作为脚本运行时你会看到输出:
C:\Users\john\Documents>python mod.py
Executing as standalone script
If Comrade Napoleon says it, it must be right.
[100, 200, 300]
arg = quux
<__main__.Foo object at 0x03450690>
但作为模块导入时则不会:
>>> import mod
>>> mod.foo('grault')
arg = grault
模块通常被设计成具备作为独立脚本运行的能力,以便测试模块中包含的功能。这被称为单元测试。例如,假设你创建了一个名为 fact.py 的模块,其中包含一个阶乘函数:
# fact.py
def fact(n):
return 1 if n == 1 else n * fact(n-1)
if (__name__ == '__main__'):
import sys
if len(sys.argv) > 1:
print(fact(int(sys.argv[1])))
该文件既可以作为模块使用,导入 fact() 函数:
>>> from fact import fact
>>> fact(6)
720
也可以作为独立脚本运行,通过命令行传递整数参数进行测试:
C:\Users\john\Documents>python fact.py 6
720
重新加载模块
出于效率考虑,每个解释器会话中模块只会被加载一次。这对函数和类定义来说通常没问题,因为它们通常构成了模块的主体内容。但模块也可以包含可执行语句(通常用于初始化)。需要注意的是,这些语句仅在模块首次导入时执行。
考虑以下 mod.py 文件:
# mod.py
a = [100, 200, 300]
print('a =', a)
>>> import mod
a = [100, 200, 300]
>>> import mod
>>> import mod
>>> mod.a
[100, 200, 300]
可以看到,后续的 import 语句不会再执行 print() 语句。(赋值语句同样不会再次执行,但如 mod.a 的最终显示所示,这无关紧要——一旦赋值完成,值就会保留。)
如果你修改了模块并需要重新加载,要么重启解释器,要么使用 importlib 模块中的 reload() 函数:
>>> 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'>
Python 包(Packages)
假设你开发了一个非常大的应用程序,其中包含许多模块。随着模块数量的增长,如果它们都被堆在一个位置,就很难管理,特别是当它们具有相似的名称或功能时。你可能会希望有一种方法来对它们进行分组和组织。
包(Packages) 允许使用点表示法对模块命名空间进行层次化结构组织。就像模块有助于避免全局变量名冲突一样,包有助于避免模块名冲突。
创建包非常简单,因为它利用了操作系统固有的层次化文件结构。考虑以下安排:
pkg/
├── mod1.py
└── mod2.py
这里有一个名为 pkg 的目录,其中包含两个模块 mod1.py 和 mod2.py。模块内容如下:
# mod1.py
def foo():
print('[mod1] foo()')
class Foo:
pass
# mod2.py
def bar():
print('[mod2] bar()')
class Bar:
pass
给定这种结构,如果 pkg 目录位于可被找到的位置(即 sys.path 中的某个目录),你就可以使用点表示法(pkg.mod1、pkg.mod2)引用这两个模块,并使用你已经熟悉的语法导入它们:
import <module_name>[, <module_name> ...]
>>> import pkg.mod1, pkg.mod2
>>> pkg.mod1.foo()
[mod1] foo()
>>> x = pkg.mod2.Bar()
>>> x
<pkg.mod2.Bar object at 0x033F7290>
from <module_name> import <name(s)>
>>> from pkg.mod1 import foo
>>> foo()
[mod1] foo()
from <module_name> import <name> as <alt_name>
>>> from pkg.mod2 import Bar as Qux
>>> x = Qux()
>>> x
<pkg.mod2.Bar object at 0x036DFFD0>
你也可以使用以下语句导入模块:
from <package_name> import <module_name>[, <module_name> ...]
from <package_name> import <module_name> as <alt_name>
>>> from pkg import mod1
>>> mod1.foo()
[mod1] foo()
>>> from pkg import mod2 as quux
>>> quux.bar()
[mod2] bar()
你甚至可以导入包本身:
>>> import pkg
>>> pkg
<module 'pkg' (namespace)>
但这没什么用处。尽管从语法上看这是正确的 Python 语句,但它几乎不做任何有用的事情。特别是,它不会将 pkg 中的任何模块放入本地命名空间:
>>> pkg.mod1
Traceback (most recent call last):
File "<pyshell#34>", line 1, in <module>
pkg.mod1
AttributeError: module 'pkg' has no attribute 'mod1'
>>> pkg.mod1.foo()
Traceback (most recent call last):
File "<pyshell#35>", line 1, in <module>
pkg.mod1.foo()
AttributeError: module 'pkg' has no attribute 'mod1'
>>> pkg.mod2.Bar()
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
pkg.mod2.Bar()
AttributeError: module 'pkg' has no attribute 'mod2'
要实际导入模块或其内容,你需要使用上面展示的形式之一。
包的初始化
如果包目录中存在一个名为 __init__.py 的文件,那么在导入包或包中的模块时,该文件会被调用。这可用于执行包初始化代码,例如初始化包级数据。
例如,考虑以下 __init__.py 文件:
# __init__.py
print(f'Invoking __init__.py for {__name__}')
A = ['quux', 'corge', 'grault']
将此文件添加到前面示例的 pkg 目录中:
现在,当导入包时,全局列表 A 会被初始化:
>>> import pkg
Invoking __init__.py for pkg
>>> pkg.A
['quux', 'corge', 'grault']
包中的模块可以通过再次导入来访问该全局变量:
# mod1.py
def foo():
from pkg import A
print('[mod1] foo() / A = ', A)
class Foo:
pass
>>> from pkg import mod1
Invoking __init__.py for pkg
>>> mod1.foo()
[mod1] foo() / A = ['quux', 'corge', 'grault']
__init__.py 还可用于实现从包中自动导入模块。例如,前面你看到 import pkg 语句只将 pkg 名称放入调用者的本地符号表,而不会导入任何模块。但如果 pkg 目录中的 __init__.py 包含以下内容:
# __init__.py
print(f'Invoking __init__.py for {__name__}')
import pkg.mod1, pkg.mod2
那么当你执行 import pkg 时,模块 mod1 和 mod2 会自动被导入:
>>> import pkg
Invoking __init__.py for pkg
>>> pkg.mod1.foo()
[mod1] foo()
>>> pkg.mod2.bar()
[mod2] bar()
注意:许多 Python 文档指出,在创建包时,包目录中必须存在
__init__.py文件。这曾经是事实。过去,__init__.py的存在本身就向 Python 表明正在定义一个包。该文件可以包含初始化代码,甚至可以为空,但必须存在。从 Python 3.3 开始,引入了隐式命名空间包(Implicit Namespace Packages)。这允许在没有
__init__.py文件的情况下创建包。当然,如果需要包初始化,该文件仍然可以存在。但它不再是必需的了。
从包中导入 *
为了便于以下讨论,我们将前面定义的包扩展为包含一些额外模块:
pkg/
├── mod1.py
├── mod2.py
├── mod3.py
└── mod4.py
现在 pkg 目录中定义了四个模块,内容如下:
# mod1.py
def foo():
print('[mod1] foo()')
class Foo:
pass
# mod2.py
def bar():
print('[mod2] bar()')
class Bar:
pass
# mod3.py
def baz():
print('[mod3] baz()')
class Baz:
pass
# mod4.py
def qux():
print('[mod4] qux()')
class Qux:
pass
(是不是很有想象力?)
你已经看到,当对模块使用 import * 时,模块中的所有对象(除了名称以下划线开头的)都会被导入到本地符号表中:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> from pkg.mod3 import *
>>> dir()
['Baz', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'baz']
>>> baz()
[mod3] baz()
>>> Baz
<class 'pkg.mod3.Baz'>
对于包,类似的语句是:
from <package_name> import *
这会做什么?
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
嗯……没做什么。你可能(如果你有任何期望的话)会以为 Python 会深入包目录,找到所有能找到的模块并全部导入。但如你所见,默认情况下并非如此。
相反,Python 遵循以下约定:如果包目录中的 __init__.py 文件包含一个名为 __all__ 的列表,那么当遇到 from <package_name> import * 语句时,该列表被视为应导入的模块列表。
对于当前示例,假设你在 pkg 目录中创建如下 __init__.py:
# pkg/__init__.py
__all__ = [
'mod1',
'mod2',
'mod3',
'mod4'
]
现在 from pkg import * 会导入全部四个模块:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> from pkg import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'mod1', 'mod2', 'mod3', 'mod4']
>>> mod2.bar()
[mod2] bar()
>>> mod4.Qux
<class 'pkg.mod4.Qux'>
使用 import * 仍然不被认为是很好的做法,无论是对包还是对模块都是如此。但这一机制至少让包的创建者能够控制在指定 import * 时会发生什么。(事实上,它甚至提供了完全禁止 import * 的能力——只需不定义 __all__ 即可。正如你所见,包的默认行为是什么都不导入。)
顺便提一下,__all__ 也可以在模块中定义,并起到相同的作用:控制 import * 时导入的内容。例如,修改 mod1.py 如下:
# pkg/mod1.py
__all__ = ['foo']
def foo():
print('[mod1] foo()')
class Foo:
pass
现在,从 pkg.mod1 执行 import * 语句只会导入 __all__ 中包含的内容:
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__']
>>> from pkg.mod1 import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'foo']
>>> foo()
[mod1] foo()
>>> Foo
Traceback (most recent call last):
File "<pyshell#37>", line 1, in <module>
Foo
NameError: name 'Foo' is not defined
现在 foo()(函数)在本地命名空间中已定义,但 Foo(类)没有,因为它不在 __all__ 中。
总结:__all__ 被包和模块用来控制 import * 时导入的内容。但默认行为不同:
- 对于包,当未定义
__all__时,import *不导入任何内容。 - 对于模块,当未定义
__all__时,import *导入所有内容(除了名称以下划线开头的)。
子包(Subpackages)
包可以包含任意深度的嵌套子包。例如,让我们对示例包目录再做一次修改:
pkg/
├── sub_pkg1/
│ ├── mod1.py
│ └── mod2.py
└── sub_pkg2/
├── mod3.py
└── mod4.py
四个模块(mod1.py、mod2.py、mod3.py 和 mod4.py)的定义与之前相同。但现在,它们被拆分到两个子包目录 sub_pkg1 和 sub_pkg2 中。
导入方式与之前相同,只是语法类似,但使用了额外的点表示法来分隔包名和子包名:
>>> import pkg.sub_pkg1.mod1
>>> pkg.sub_pkg1.mod1.foo()
[mod1] foo()
>>> from pkg.sub_pkg1 import mod2
>>> mod2.bar()
[mod2] bar()
>>> from pkg.sub_pkg2.mod3 import baz
>>> baz()
[mod3] baz()
>>> from pkg.sub_pkg2.mod4 import qux as grault
>>> grault()
[mod4] qux()
此外,一个子包中的模块可以引用同级子包中的对象(如果同级子包包含你需要的功能)。例如,假设你想从 mod3(在 sub_pkg2 中)导入并执行 foo() 函数(在 mod1 中定义)。你可以使用绝对导入:
# pkg/sub_pkg2/mod3.py
def baz():
print('[mod3] baz()')
class Baz:
pass
from pkg.sub_pkg1.mod1 import foo
foo()
>>> from pkg.sub_pkg2 import mod3
[mod1] foo()
>>> mod3.foo()
[mod1] foo()
或者你可以使用相对导入,其中 .. 表示上一级包。从 mod3.py(位于子包 sub_pkg2 中)来看:
..表示父包(pkg)..sub_pkg1表示父包的sub_pkg1子包
# pkg/sub_pkg2/mod3.py
def baz():
print('[mod3] baz()')
class Baz:
pass
from .. import sub_pkg1
print(sub_pkg1)
from ..sub_pkg1.mod1 import foo
foo()
>>> from pkg.sub_pkg2 import mod3
<module 'pkg.sub_pkg1' (namespace)>
[mod1] foo()
结论
在本教程中,你涵盖了以下主题:
- 如何创建 Python 模块
- Python 解释器搜索模块的位置
- 如何使用
import语句访问模块中定义的对象 - 如何创建可作为独立脚本执行的模块
- 如何将模块组织成包和子包
- 如何控制包的初始化
希望这能帮助你更好地理解如何访问 Python 中众多第三方和内置模块所提供的功能。
此外,如果你正在开发自己的应用程序,创建自己的模块和包将有助于组织和模块化你的代码,从而使编码、维护和调试变得更加容易。
如果你想了解更多,请查阅 Python.org 上的以下文档:
愉快Python吧!