geekdoc-python-zh/docs/realpython/python-modules-packages.md

30 KiB
Raw Blame History

Python 模块和包——简介

原文:https://realpython.com/python-modules-packages/

*立即观看**本教程有真实 Python 团队创建的相关视频课程。和书面教程一起看,加深理解: Python 模块和包:简介

本文探索了 Python 模块和 Python ,这两种机制促进了模块化编程

模块化编程指的是将一个庞大、笨拙的编程任务分解成独立、更小、更易管理的子任务或模块的过程。然后,可以像构建模块一样将各个模块拼凑在一起,创建一个更大的应用程序。

在大型应用程序中,模块化代码有几个优点:

  • 简单性:一个模块通常专注于问题的一个相对较小的部分,而不是关注手头的整个问题。如果你在一个模块上工作,你将会有一个更小的问题域去思考。这使得开发更容易,更不容易出错。

  • **可维护性:**模块通常被设计成在不同的问题域之间强制执行逻辑边界。如果以最小化相互依赖性的方式编写模块,那么对单个模块的修改对程序的其他部分产生影响的可能性就会降低。(您甚至可以在不了解模块之外的应用程序的情况下对模块进行更改。)这使得一个由许多程序员组成的团队在一个大型应用程序上协作工作变得更加可行。

  • **可重用性:**在单个模块中定义的功能可以很容易地被应用程序的其他部分重用(通过适当定义的接口)。这消除了复制代码的需要。

  • **作用域:**模块通常定义一个单独的 命名空间 ,这有助于避免程序不同区域的标识符之间的冲突。(Python 的禅的信条之一是名称空间是一个非常棒的想法——让我们做更多这样的事情吧!)

函数模块都是 Python 中促进代码模块化的构造。

免费 PDF 下载: Python 3 备忘单

Python 模块:概述

在 Python 中,实际上有三种不同的方式来定义模块:

  1. 一个模块可以用 Python 本身编写。
  2. 一个模块可以用 C 编写,在运行时动态加载,像re ( 正则表达式 )模块。
  3. 一个内置的模块本质上包含在解释器中,就像 itertools模块一样。

一个模块的内容在所有三种情况下都以相同的方式被访问:用import语句。

这里,重点将主要放在用 Python 编写的模块上。用 Python 编写的模块最酷的一点是它们非常容易构建。您所需要做的就是创建一个包含合法 Python 代码的文件,然后给这个文件起一个扩展名为.py的名字。就是这样!不需要特殊的语法或巫术。

例如,假设您创建了一个名为mod.py的文件,其中包含以下内容:

mod . pyT3】

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>

Remove ads

模块搜索路径

继续上面的例子,让我们看看 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放在一个依赖于安装的目录中,根据操作系统的不同,您可能有也可能没有写权限

实际上还有一个额外的选项:您可以将模块文件放在您选择的任何目录中,然后在运行时修改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>为前缀时才是可访问的,如下所示。

在下面的import语句之后,mod被放入局部符号表。因此,mod在呼叫者的当地语境中有意义:

>>> import mod
>>> mod
<module 'mod' from 'C:\\Users\\john\\Documents\\Python\\doc\\mod.py'>

但是sfoo保留在模块的私有符号表中,在本地上下文中没有意义:

>>> 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> ...]

Remove ads

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>

也可以import单个对象,但用备用名输入到本地符号表中:

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

模块内容可以从函数定义中导入。在这种情况下,import不会发生,直到函数被调用:

>>> 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

最后,带有except ImportError 子句的 try语句可用于防止不成功的import尝试:

>>> try:
...     # Non-existent module
...     import baz
... except ImportError:
...     print('Module not found')
...

Module not found
>>> try:
...     # Existing module, but non-existent object
...     from mod import baz
... except ImportError:
...     print('Object not found in module')
...

Object not found in module

Remove ads

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()的第一次调用是如何列出几个名称的,这些名称是自动定义的,并且在解释器启动时已经存在于名称空间中。随着新名称的定义(quxBarx),它们会出现在后续的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 . pyT3】

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>

没有错误,所以它显然是有效的。当然,这不是很有趣。正如所写的,只有定义了对象。T2 不会对它们做任何事情,也不会产生任何输出。

让我们修改上面的 Python 模块,使它在作为脚本运行时生成一些输出:

mod . pyT3】

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 将特殊的 dunder 变量 __name__ 设置为模块的名称。然而,如果一个文件作为独立脚本运行,__name__被(创造性地)设置为字符串'__main__'。利用这一事实,您可以在运行时辨别出哪种情况,并相应地改变行为:

mod . pyT3】

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

Remove ads

重新加载模块

出于效率的原因,一个模块在每个解释器会话中只加载一次。这对于函数和类定义来说很好,它们通常构成了模块的大部分内容。但是模块也可以包含可执行语句,通常用于初始化。请注意,这些语句只会在第一次导入模块时执行。

考虑下面的文件mod.py:

mod . pyT3】

a = [100, 200, 300]
print('a =', a)
>>> import mod
a = [100, 200, 300]
>>> import mod
>>> import mod

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

在后续导入中不执行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 包

假设您开发了一个包含许多模块的非常大的应用程序。随着模块数量的增长,如果将它们放在一个位置,就很难跟踪所有的模块。如果它们具有相似的名称或功能,尤其如此。您可能希望有一种方法来对它们进行分组和组织。

允许使用点符号对模块名称空间进行层次化构造。同样,模块有助于避免全局变量名之间的冲突,有助于避免模块名之间的冲突。

创建一个非常简单,因为它利用了操作系统固有的层次文件结构。考虑以下安排:

Image of a Python package

这里有一个名为pkg的目录,包含两个模块mod1.pymod2.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.mod1pkg.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 <modules_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'

要实际导入模块或它们的内容,您需要使用上面显示的表单之一。

Remove ads

包初始化

如果包目录中有一个名为__init__.py的文件,当导入包或包中的一个模块时,它会被调用。这可用于执行包初始化代码,例如包级数据的初始化。

例如,考虑下面的__init__.py文件:

init。pyT3】

print(f'Invoking __init__.py for {__name__}')
A = ['quux', 'corge', 'grault']

让我们将这个文件添加到上面示例中的pkg目录中:

Illustration of hierarchical file structure of Python packages

现在,当包被导入时,全局列表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。pyT3】

print(f'Invoking __init__.py for {__name__}')
import pkg.mod1, pkg.mod2

然后当您执行import pkg时,模块mod1mod2被自动导入:

>>> 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 开始,隐式名称空间包被引入。这些允许创建一个没有任何__init__.py文件的包。当然,如果需要包初始化,它可以仍然存在。但是不再需要了。

从包中导入*

出于以下讨论的目的,前面定义的包被扩展为包含一些附加模块:

Illustration of hierarchical file structure of Python packages

现在在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。pyT3】

__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 *时会发生什么。(事实上,它提供了完全不允许它的能力,简单地拒绝定义__all__。正如您所看到的,包的默认行为是不导入任何东西。)

顺便说一下,__all__也可以在模块中定义,其目的相同:控制用import *导入的内容。例如,将mod1.py修改如下:

pkg/mod1.py

__all__ = ['foo']

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

class Foo:
    pass

现在来自pkg.mod1import *语句将只导入包含在__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 *会导入所有内容(除了——你猜对了——以下划线开头的名字)。

Remove ads

子包

包可以包含任意深度的嵌套子包。例如,让我们对示例目录进行如下修改:

Illustration of hierarchical file structure of Python packages

四个模块(mod1.pymod2.pymod3.pymod4.py)的定义如前。但是现在,它们没有被集中到pkg目录中,而是被分成两个子包目录,sub_pkg1sub_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中导入并执行函数foo()(在模块mod1中定义)。您可以使用绝对导入:

pkg/sub _ _ pkg 2/mod 3 . pyT3】

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 _ _ pkg 2/mod 3 . pyT3】

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语句访问模块中定义的对象
  • 如何创建可作为独立脚本执行的模块
  • 如何将模块组织成子包
  • 如何控制包的初始化

免费 PDF 下载: Python 3 备忘单

这有望让您更好地理解如何获得 Python 中许多第三方和内置模块的可用功能。

此外,如果你正在开发自己的应用程序,创建自己的模块将帮助你组织和模块化你的代码,这使得编码、维护和调试更容易。

如果您想了解更多信息,请在Python.org查阅以下文档:

快乐的蟒蛇!

立即观看本教程有真实 Python 团队创建的相关视频课程。和书面教程一起看,加深理解: Python 模块和包:简介*****