Python 模块基础回顾

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在这个实验中,你将学习 Python 模块的基础知识。模块是包含函数、类和变量定义的 Python 文件,可在其他 Python 程序中使用。它们有助于将代码组织成逻辑单元,并提高代码的可重用性。

在本实验结束时,你将了解如何创建自己的模块,以各种方式导入它们,并掌握影响代码的重要模块加载行为。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ModulesandPackagesGroup(["Modules and Packages"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python/BasicConceptsGroup -.-> python/variables_data_types("Variables and Data Types") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/ModulesandPackagesGroup -.-> python/importing_modules("Importing Modules") python/ModulesandPackagesGroup -.-> python/creating_modules("Creating Modules") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") subgraph Lab Skills python/variables_data_types -.-> lab-132528{{"Python 模块基础回顾"}} python/function_definition -.-> lab-132528{{"Python 模块基础回顾"}} python/importing_modules -.-> lab-132528{{"Python 模块基础回顾"}} python/creating_modules -.-> lab-132528{{"Python 模块基础回顾"}} python/classes_objects -.-> lab-132528{{"Python 模块基础回顾"}} end

创建一个简单的模块

让我们通过创建一个简单的模块来开启 Python 模块的学习之旅。在 Python 中,模块本质上是一个扩展名为 .py 的文件,其中包含 Python 代码。你可以将其视为一个容器,用于将相关的函数、类和变量组合在一起。这会让你的代码更有条理,也更易于管理,尤其是在项目规模不断扩大时。

  1. 首先,打开 WebIDE。打开后,你需要创建一个新文件。为此,点击菜单栏中的 “File”,然后选择 “New File”。将这个新文件命名为 simplemod.py,并将其保存到 /home/labex/project 目录下。这个目录将用于存放与本实验相关的所有文件。

  2. 现在,让我们在新创建的 simplemod.py 文件中添加一些代码。下面的代码定义了一些你在 Python 模块中常见的基本元素。

## simplemod.py

x = 42        ## A global variable

## A simple function
def foo():
    print('x is', x)

## A simple class
class Spam:
    def yow(self):
        print('Yow!')

## A scripting statement
print('Loaded simplemod')

在这段代码中:

  • x = 42 创建了一个名为 x 的全局变量,并将其赋值为 42。全局变量可以在模块内的任何地方访问。
  • foo() 函数用于打印全局变量 x 的值。函数是可重复使用的代码块,用于执行特定任务。
  • Spam 类是创建对象的蓝图。它有一个名为 yow() 的方法,该方法仅打印字符串 'Yow!'。方法是属于类的函数。
  • print('Loaded simplemod') 语句是一个脚本语句。一旦模块被加载,它就会执行,这有助于我们确认模块已成功加载。
  1. 添加代码后,保存文件。你可以通过按下键盘上的 Ctrl+S 组合键,或者从菜单中选择 “File” > “Save” 来完成保存操作。保存文件可确保你所做的所有更改都被保留。

让我们仔细看看这个模块包含的内容:

  • 一个值为 42 的全局变量 x。如果正确导入,这个变量可以在整个模块中使用,甚至可以从其他模块访问。
  • 一个打印 x 值的函数 foo()。函数对于执行重复性任务非常有用,无需多次编写相同的代码。
  • 一个带有 yow() 方法的类 Spam。类和方法是面向对象编程中的基本概念,它们允许你创建复杂的数据结构和行为。
  • 一个在模块加载时执行的 print 语句。这个语句可以直观地表明模块已成功加载到 Python 环境中。

底部的 print 语句将帮助我们观察模块何时被加载,这对于调试和理解 Python 中模块的工作原理非常重要。

✨ 查看解决方案并练习

导入和使用模块

既然我们已经创建了一个模块,现在是时候了解如何导入它并使用其组件了。在 Python 中,模块是包含 Python 定义和语句的文件。当你导入一个模块时,你可以访问其中定义的所有函数、类和变量。这使你能够重用代码并更有效地组织程序。

  1. 首先,我们需要在 WebIDE 中打开一个新的终端。这个终端将作为我们运行 Python 命令的工作空间。要打开新终端,请点击 “Terminal” > “New Terminal”。

  2. 终端打开后,我们需要启动 Python 解释器。Python 解释器是一个读取并执行 Python 代码的程序。要启动它,请在终端中输入以下命令并按回车键:

python3
  1. 现在 Python 解释器已经运行,我们可以导入我们的模块了。在 Python 中,我们使用 import 语句将模块引入到当前程序中。在 Python 解释器中输入以下命令:
>>> import simplemod
Loaded simplemod

你会注意到输出中出现了 “Loaded simplemod”。这是因为我们的 simplemod 模块中的 print 语句在模块加载时会执行。当 Python 导入一个模块时,它会运行该模块中的所有顶级代码,包括任何 print 语句。

  1. 导入模块后,我们可以使用点号表示法来访问其组件。点号表示法是 Python 中访问对象属性(变量和函数)的一种方式。在这种情况下,模块就是一个对象,其函数、变量和类就是它的属性。以下是一些如何访问 simplemod 模块不同组件的示例:
>>> simplemod.x
42
>>> simplemod.foo()
x is 42
>>> spam_instance = simplemod.Spam()
>>> spam_instance.yow()
Yow!

第一行中,我们访问了 simplemod 模块中定义的变量 x。第二行中,我们调用了 simplemod 模块中的函数 foo。第三行和第四行中,我们创建了 simplemod 模块中定义的 Spam 类的一个实例,并调用了其方法 yow

  1. 有时,你在尝试导入模块时可能会遇到 ImportError。当 Python 找不到你要导入的模块时,就会出现这个错误。要确定 Python 在哪里查找模块,你可以检查 sys.path 变量。sys.path 变量是一个目录列表,Python 在查找模块时会搜索这些目录。在 Python 解释器中输入以下命令:
>>> import sys
>>> sys.path
['', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

列表中的第一个元素(空字符串)表示当前工作目录。这就是 Python 查找 simplemod.py 文件的地方。如果你的模块不在 sys.path 列出的任何一个目录中,Python 将无法找到它,你就会得到一个 ImportError。确保你的 simplemod.py 文件位于当前工作目录或 sys.path 中的其他目录之一。

理解模块加载行为

在 Python 中,模块的加载方式有一些有趣的特性。在这一步,我们将探索这些行为,以了解 Python 是如何管理模块加载的。

  1. 首先,让我们看看在同一个 Python 解释器会话中再次导入模块时会发生什么。当你启动 Python 解释器时,就像是打开了一个可以运行 Python 代码的工作空间。一旦你导入了一个模块,再次导入它似乎会重新加载该模块,但事实并非如此。
>>> import simplemod

注意,这次你没有看到 “Loaded simplemod” 的输出。这是因为 Python 在每个解释器会话中只加载一次模块。后续的 import 语句不会重新加载模块。Python 会记住它已经加载了该模块,因此不会再次执行加载过程。

  1. 导入模块后,你可以修改其中的变量。Python 中的模块就像一个容器,包含变量、函数和类。导入模块后,你可以像处理其他 Python 对象一样访问和更改其变量。
>>> simplemod.x
42
>>> simplemod.x = 13
>>> simplemod.x
13
>>> simplemod.foo()
x is 13

在这里,我们首先检查 simplemod 模块中变量 x 的值,最初它是 42。然后我们将其值更改为 13 并验证更改已生效。当我们调用模块中的 foo 函数时,它反映了 x 的新值。

  1. 再次导入模块不会重置我们对其变量所做的更改。即使我们再次尝试导入该模块,Python 也不会重新加载它,因此我们对其变量所做的更改仍然保留。
>>> import simplemod
>>> simplemod.x
13
  1. 如果你想强制重新加载一个模块,你需要使用 importlib.reload() 函数。有时,你可能对模块的代码进行了更改,并希望立即看到这些更改生效。importlib.reload() 函数可以帮助你实现这一点。
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
>>> simplemod.x
42
>>> simplemod.foo()
x is 42

模块已被重新加载,x 的值已重置为 42。这表明模块已从其源代码重新加载,所有变量都已恢复到初始状态。

  1. Python 在 sys.modules 字典中跟踪所有已加载的模块。这个字典就像一个注册表,Python 会存储当前解释器会话期间加载的所有模块的信息。
>>> 'simplemod' in sys.modules
True
>>> sys.modules['simplemod']
<module 'simplemod' from 'simplemod.py'>

通过检查模块名称是否在 sys.modules 字典中,你可以查看该模块是否已加载。通过使用模块名称作为键访问该字典,你可以获取有关该模块的信息。

  1. 你可以从这个字典中移除一个模块,以强制 Python 在下次导入时重新加载它。如果你从 sys.modules 字典中移除一个模块,Python 会忘记它已经加载了该模块。因此,下次你尝试导入它时,Python 将从其源代码重新加载它。
>>> del sys.modules['simplemod']
>>> import simplemod
Loaded simplemod
>>> simplemod.x
42

该模块被重新加载了,因为它已从 sys.modules 中移除。这是确保你使用模块代码的最新版本的另一种方法。

使用 from module import 语法

在 Python 中,有多种从模块导入组件的方式。其中一种方式是 from module import 语法,我们将在本节中探讨这种语法。

当你从模块导入组件时,通常最好从一个全新的状态开始。这能确保没有之前操作遗留的变量或设置干扰我们当前的实验。

  1. 重启 Python 解释器以获得全新状态:
>>> exit()

此命令会退出当前的 Python 解释器会话。退出后,我们将启动一个新会话以确保环境是全新的。

python3

这个 bash 命令会启动一个新的 Python 3 解释器会话。现在我们有了一个全新的 Python 环境,可以开始从模块导入组件了。

  1. 使用 from module import 语法从模块导入特定组件:
>>> from simplemod import foo
Loaded simplemod
>>> foo()
x is 42

在这里,我们使用 from simplemod import foo 语句仅从 simplemod 模块导入 foo 函数。注意,尽管我们只请求了 foo 函数,但整个 simplemod 模块都被加载了。这可以从 “Loaded simplemod” 输出看出来。原因是 Python 需要加载整个模块才能访问 foo 函数。

  1. 使用 from module import 时,你无法直接访问模块本身:
>>> simplemod.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'simplemod' is not defined

当我们使用 from module import 语法时,我们只是将指定的组件直接引入到我们的命名空间中。模块名本身并没有被导入。所以,当我们尝试访问 simplemod.foo() 时,Python 不识别 simplemod,因为它不是以这种方式导入的。

  1. 你可以一次导入多个组件:
>>> from simplemod import x, foo
>>> x
42
>>> foo()
x is 42

from module import 语法允许我们在一条语句中从模块导入多个组件。这里,我们从 simplemod 模块同时导入了变量 x 和函数 foo。导入后,我们可以在代码中直接访问这些组件。

  1. 当你从模块导入一个变量时,你是在创建一个对该对象的新引用,而不是与模块中的变量建立链接:
>>> x = 13  ## Change the local variable x
>>> x
13
>>> foo()
x is 42  ## The function still uses the module's x, not your local x

当我们从模块导入一个变量时,实际上是在我们的局部命名空间中创建了一个对同一对象的新引用。所以,当我们将局部变量 x 更改为 13 时,它不会影响 simplemod 模块内的 x 变量。foo() 函数仍然引用模块中的 x 变量,其值为 42。理解这个概念对于避免代码中的混淆至关重要。

探索模块重新加载的局限性

模块重新加载是 Python 中一个有用的特性,但它也有一些局限性,尤其是在处理类时。在本节中,我们将逐步探索这些局限性。理解这些局限性对于开发和生产环境都至关重要。

  1. 重启 Python 解释器:
    首先,我们需要重启 Python 解释器。这一步很重要,因为它确保我们从一个全新的状态开始。当你重启解释器时,所有之前导入的模块和变量都会被清除。要退出当前的 Python 解释器,使用 exit() 命令。然后,在终端中使用 python3 命令启动一个新的 Python 解释器会话。
>>> exit()
python3
  1. 导入模块并创建 Spam 类的实例:
    现在我们有了一个全新的 Python 解释器会话,我们将导入 simplemod 模块。导入模块后,我们就可以使用该模块中定义的类、函数和变量。导入模块后,我们将创建 Spam 类的一个实例,并调用其 yow() 方法。这将帮助我们了解该类的初始行为。
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Yow!
  1. 现在让我们修改模块中的 Spam 类。退出 Python 解释器:
    接下来,我们要对 simplemod 模块中的 Spam 类进行修改。在这之前,我们需要退出 Python 解释器。这是因为我们要对模块的源代码进行修改,然后观察这些修改如何影响类的行为。
>>> exit()
  1. 在 WebIDE 中打开 simplemod.py 文件并修改 Spam 类:
    在 WebIDE 中打开 simplemod.py 文件。这里是 simplemod 模块源代码所在的位置。我们将修改 Spam 类的 yow() 方法,使其打印不同的消息。这个更改将帮助我们观察重新加载模块后类的行为如何变化。
## simplemod.py
## ... (保留文件其余部分不变)

class Spam:
    def yow(self):
        print('More Yow!')  ## 从 'Yow!' 更改而来
  1. 保存文件并返回终端。启动 Python 解释器并创建一个新实例:
    simplemod.py 文件进行修改后,保存它。然后,返回终端并启动一个新的 Python 解释器会话。再次导入 simplemod 模块,并创建 Spam 类的一个新实例。调用新实例的 yow() 方法,以查看更新后的行为。
python3
>>> import simplemod
Loaded simplemod
>>> t = simplemod.Spam()
>>> t.yow()
More Yow!
  1. 现在让我们演示重新加载会发生什么:
    为了了解模块重新加载的工作原理,我们将使用 importlib.reload() 函数。这个函数允许我们重新加载之前导入的模块。重新加载模块后,我们将查看对 Spam 类所做的更改是否得到反映。
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
  1. 退出 Python,再次修改文件,然后测试两个实例:
    再次退出 Python 解释器。然后,对 simplemod.py 文件中的 Spam 类进行另一次修改。之后,我们将测试 Spam 类的旧实例和新实例,以查看它们的行为如何。
>>> exit()
  1. 更新 simplemod.py 文件:
    再次打开 simplemod.py 文件,并修改 Spam 类的 yow() 方法,使其打印不同的消息。这个更改将帮助我们进一步理解模块重新加载的局限性。
## simplemod.py
## ... (保留文件其余部分不变)

class Spam:
    def yow(self):
        print('Even More Yow!')  ## 再次更改
  1. 保存文件并返回终端:
    保存对 simplemod.py 文件所做的更改,然后返回终端。启动一个新的 Python 解释器会话,导入 simplemod 模块,并创建 Spam 类的一个新实例。调用新实例的 yow() 方法,以查看更新后的行为。
python3
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Even More Yow!

>>> ## 不关闭 Python 退出,编辑文件
  1. 不关闭 Python,在 WebIDE 中打开 simplemod.py 并进行更改:
    不关闭 Python 解释器,在 WebIDE 中打开 simplemod.py 文件,并对 Spam 类的 yow() 方法进行另一次修改。这将帮助我们观察重新加载模块后现有实例和新实例的行为如何变化。
## simplemod.py
## ... (保留文件其余部分不变)

class Spam:
    def yow(self):
        print('Super Yow!')  ## 再一次更改
  1. 保存文件并返回 Python 解释器:
    保存对 simplemod.py 文件所做的更改,然后返回 Python 解释器。使用 importlib.reload() 函数重新加载 simplemod 模块。然后,测试 Spam 类的旧实例和新实例,以查看它们的行为如何。
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>

>>> ## 尝试旧实例
>>> s.yow()
Even More Yow!  ## 仍然使用旧的实现

>>> ## 创建一个新实例
>>> t = simplemod.Spam()
>>> t.yow()
Super Yow!  ## 使用新的实现

注意,旧实例 s 仍然使用旧的实现,而新实例 t 使用新的实现。这是因为重新加载模块不会更新类的现有实例。当创建一个类实例时,它会存储对当时类对象的引用。重新加载模块会创建一个新的类对象,但现有实例仍然引用旧的类对象。

  1. 你还可以观察到其他异常行为:
    我们可以通过使用 isinstance() 函数进一步观察模块重新加载的局限性。这个函数用于检查一个对象是否是某个特定类的实例。重新加载模块后,我们会发现旧实例 s 不再被视为新的 simplemod.Spam 类的实例,而新实例 t 是。
>>> isinstance(s, simplemod.Spam)
False
>>> isinstance(t, simplemod.Spam)
True

这表明重新加载后,simplemod.Spam 引用的类对象与创建 s 时使用的类对象不同。

这些局限性使得模块重新加载主要适用于开发和调试,不建议用于生产代码。在生产环境中,确保类的所有实例使用相同的、最新的实现非常重要。模块重新加载可能会导致不一致的行为,这可能难以调试和维护。

总结

在本次实验中,你学习了 Python 中使用模块的基础知识。你学会了如何创建一个包含变量、函数和类的 .py 文件作为 Python 模块,以及如何使用 import 语句导入模块。你还了解到 Python 在每个解释器会话中只会加载一次模块,并且可以使用 importlib.reload() 强制重新加载。

此外,你探索了用于跟踪已加载模块的 sys.modules 字典,使用 from module import 语法导入特定组件,并掌握了模块重新加载的局限性,特别是在处理类时的局限性。这些概念是将 Python 代码组织成可重用组件的基础,对于维护代码结构和在大型应用程序中提高代码可重用性至关重要。