Skip to content

9. 导入

在应用程序练习的第 1 版中遇到的错误,促使我们更深入地探讨 [import] 语句的作用。

Image

9.1. 脚本 [import_01]

[imported]脚本将被各种脚本(也称为模块)导入:

Image

1
2
3
4
5
#  imported module
#  this instruction will be executed each time the module is imported
print("2")
#  variable belonging to the imported module
x=4

模块在被导入时会被执行。因此,当 [被导入的] 模块被导入时:

  • 第 3 行的输出将发生;
  • 第 5 行中的变量 x 将被赋值;

[main_01]脚本如下:

1
2
3
4
#  an imported module is executed
import imported
#  use of the x variable of the imported module
print(imported.x)
  • 第 2 行:导入 [imported] 模块。这将导致其被执行:
    • 将显示值 2;
    • 变量 x 被创建,其值为 4;
  • 第 4 行:使用了来自导入模块的变量 x;

在 PyCharm 中,报告了一个错误:

[1] 中,PyCharm 提示无法识别 [imported] 模块。从技术角度讲,这意味着包含 [imported] 模块的文件夹未被添加到 PyCharm 的 Python 路径中。Python 路径是指用于搜索导入模块的文件夹集合。 要解决此问题,只需将包含 [imported] 模块的文件夹设置为 [Root Sources] 文件夹,本例中即 [import/01] 文件夹:

Image

Image

完成此步骤后,[import/01] 文件夹将被添加到 PyCharm 的 Python 路径中,错误提示随之消失:

Image

  • [1] 中,[01] 文件夹的颜色已发生变化;
  • [2-3] 中,已不再显示错误;

执行结果如下:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_01.py
2
4

Process finished with exit code 0

注释

  • 第 2 行是执行导入模块的结果;
  • 第 3 行显示了来自导入模块的变量 x 的值;

本示例的核心要点在于:导入的模块(或脚本)会被执行,这是一个重要的概念。

[main_02]脚本如下:

1
2
3
4
#  import the x variable from the imported module
from imported import x
#  we display it
print(x)
  • 第 2 行使用了不同的导入语法 [from 模块 import 对象1, 对象2, …]。这里,我们导入变量 [imported.x]。使用这种语法,变量 x 成为 [main_02] 脚本中的变量。我们不再需要在其前面加上其模块 [imported]
  • 第 4 行:我们打印来自 [main_02] 的变量 x;

执行结果如下:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_02.py
2
4

Process finished with exit code 0

[main_03] 脚本如下:

1
2
3
4
#  import everything visible in the imported module
from imported import *
#  use the x variable of the imported module
print(x)

第 2 行中的 [import *] 语法表示我们将从被导入的模块中导入所有可见对象(变量、函数)。

结果如下:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/01/main_03.py
2
4

Process finished with exit code 0

[main_04] 脚本如下:

1
2
3
4
5
#  import the x variable from the imported module
#  and rename it y
from imported import x as y
#  display variable y
print(y) 

第 3 行表明我们可以从 imported 模块导入一个对象,并为其指定一个别名。在此,变量 [imported.x] 变成了变量 [main_04.y]。结果与之前相同。

9.2. 脚本 [import_02]

Image

导入的模块 [module1.py] 内容如下:

1
2
3
#  a function
def f1():
    print("f1")

导入的模块定义了一个函数,这是常见的情况。

脚本 [main_01] 如下所示:

1
2
3
4
#  import
import module1
#  execution f1
module1.f1()
  • 第 2 行:导入该模块。它将被执行。此处不会显示任何内容;
  • 第 4 行:执行从导入模块中调用的 [f1] 函数;

执行结果如下:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/02/main_01.py
f1

Process finished with exit code 0

注意:为防止 PyCharm 在第 2 行导入时报错,您必须将包含 [module1] 的文件夹添加到 PyCharm 的 [根目录] 中:

Image

[1] 中,放置在 [源代码根目录] 中的 [02] 文件夹会变为蓝色。请注意,此处报告的错误并不影响脚本的正常运行。 实际上,当执行 [main_0x] 脚本时,该脚本所在的文件夹会自动添加到 Python 路径中。因此,[module1] 会被找到。从现在起,当截图中某个文件夹显示为蓝色时,表示它已被放置在 PyCharm 的 [源代码根目录] 中

[main_02] 脚本内容如下:

1
2
3
4
#  import
from module1 import f1
#  execution f1
f1()
  • 第 2 行从模块 [module1] 中导入函数 [f1]
  • 第 4 行调用了函数 f1;

结果与 [main_01] 脚本的结果完全相同。

9.3. 脚本 [import_03]

Image

[03] 位于项目的 [Root Sources] 目录下。

新脚本将导入 [module2] 模块,该模块并不位于与它们相同的文件夹中。

[module2] 脚本内容如下:

1
2
3
#  a function
def f2():
    print("f2")

因此,该脚本定义了一个函数 [f2]

[main_01]脚本如下:

1
2
3
4
#  class2 module import
import dir1.module2
#  execution f2
dir1.module2.f2()
  • 第 2 行:我们使用一种特殊表示法来指示如何查找 [module2] 模块。[dir1.module2] 应理解为路径 [dir1/module2]:要查找 [module2],请从当前脚本所在的文件夹 [main_01] 开始,然后进入 [dir1],在那里你会找到 [module2]。 请注意,路径的起点是正在导入的脚本所在的文件夹;
  • 第 4 行:执行 [module2] 中的函数 [f2]

结果如下:

1
2
3
4
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/03/main_01.py
f2

Process finished with exit code 0

第 2 行,[f2] 函数的结果。

[main_02]脚本如下:

1
2
3
4
#  import module dir1.module2 and rename it
import dir1.module2 as module2
#  execution f2
module2.f2()

在第 2 行,我们将模块 [dir1.module] 重命名,以简化第 4 行的代码。

[main_03]脚本如下:

1
2
3
4
#  import function f2 from module dir1.module2
from dir1.module2 import f2
#  execution f2
f2()

这次,在第 2 行,我们只导入了 [f2] 函数,该函数随后成为 [main_03] 脚本中的一个函数(第 4 行)。

所有这些脚本在 PyCharm 环境中的运行效果与在 Python 控制台中一样好。原因是无论哪种情况,执行脚本所在的目录(此处为 [03] 目录)都是 Python 路径的一部分。因此,系统能够找到 [dir1/module2] 目录。

9.4. 脚本 [import_04]

Image

在此,[dir1][dir2] 文件夹已被放置在 PyCharm 项目的 [Root Sources] 中。

首先导入的模块是 [module3]

1
2
3
#  a function
def f3():
    print("f3")

导入的第二个模块是 [module4]

1
2
3
4
5
6
from module3 import f3

#  a function
def f4():
    f3()
    print("f4")
  • 第 1 行:我们从 [module3] 导入函数 [f3]。这里,[module3] 是可见的,因为我们将其目录 [dir1] 放置在了 [Root Sources] 中;
  • 第 4–6 行:我们定义了一个函数 [f4],该函数调用来自 [module3] 的函数 [f3]

主脚本 [main_01] 如下所示:

1
2
3
4
#  import module4
from module4 import f4
#  execution f4
f4()
  • 第 2 行:导入 [module4] 模块。这是可见的,因为我们将它的目录 [dir2] 放置在 PyCharm 的 [Root Sources] 中;
  • 第 4 行:执行来自 [module4] [f4] 函数;

在 PyCharm 中运行 [main_01] 的结果如下:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/04/main_01.py
f3
f4

Process finished with exit code 0

现在,让我们在 Python 终端(控制台)中运行 [main_01]

Image

结果如下:

1
2
3
4
5
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\04>python main_01.py
Traceback (most recent call last):
  File "main_01.py", line 2, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'

发生了什么?Python 终端并不了解 PyCharm 的 Python 路径或 [源代码根目录]。它拥有自己的 Python 路径。在这个路径中,总是包含正在执行的脚本所在的文件夹,在本例中即 [main_01] 脚本。因此,它知道 [import/04] 文件夹。在执行的脚本中,它找到了这一行:


from module4 import f4

Python 解释器会在其 Python 路径中的文件夹内查找 [module4]。然而,[module4] 并不位于 [import/04] 文件夹中尽管该文件夹确实在 Python 路径内),而是位于 [import/04/dir2] 文件夹中,而该文件夹不在 Python 路径内。因此导致了错误。

因此,我们遇到了一个曾遇到过的问题:在 PyCharm 中能正常运行的脚本,在 Python 终端中可能会崩溃。这是一个反复出现的问题,我们需要解决它。

9.5. 脚本 [import_05]

Image

注意[dir1][dir2] 文件夹已添加到 Python 路径中。请注意这里已经存在冲突:[module3][module4] 在 PyCharm 的 Python 路径中将出现在两个位置:

  • 对于 [module3],分别位于 [import/04/dir1] [import/05/dir1] 中;
  • 位于 [import/04/dir2] [import/05/dir2] 中的 [module4]

然后我们可以将 [import/04/dir1][import/04/dir2] 从 PyCharm 项目的 [根源文件] 中移除。 事实证明,此处的 [import/05/dir1][import/04/dir1] 的副本([dir2] 也是如此),因此不会出现问题。不过值得注意的是,在 PyCharm 内部,必须注意 [Root Sources] 中的文件夹列表,以避免冲突。

[main_01]脚本内容如下:

import sys
#  modify sys.path to include folders
#  containing the classes to be imported
sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")
#  import module4
from module4 import f4
#  execution f4
f4()

我们正在尝试解决 Python 路径问题。我们希望找到一种在 PyCharm 中和在 Python 终端中一样好用的方案。为此,我们将自行配置。

  • 第 4–6 行:我们将目录 [., ./dir1, ./dir2] 添加到 Python 路径中。要使此操作生效,运行时的当前目录必须是 [import/05] 目录。在 PyCharm 中这通常成立,但在 Python 终端中则未必如此,我们稍后将看到;
  • 第 8 行:我们导入 [module4]。根据刚才的操作,它应该位于 [./dir2] 目录中;

在 PyCharm 中执行将得到以下结果:

1
2
3
4
5
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/main_01.py
f3
f4

Process finished with exit code 0

现在,在 Python 终端中:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>python main_01.py
f3
f4

第 1 行,执行目录为 [import/05]

现在让我们从 [import/05] 目录向上移动一层:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\05>cd ..
 
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_01.py
Traceback (most recent call last):
  File "05/main_01.py", line 8, in <module>
    from module4 import f4
ModuleNotFoundError: No module named 'module4'
  • 第 2 行:当执行 [main_01] 时,我们已不在 [import/05] 文件夹中,而是在 [import] 文件夹中。然而,我们写的是:

sys.path.append(".")
sys.path.append("./dir1")
sys.path.append("./dir2")

这会将文件夹 [import, import/dir1, import/dir2] 添加到 Python 路径中,这完全不是我们想要的结果。请注意,将不存在的文件夹(import/dir1、import/dir2)添加到 Python 路径中并不会引发错误。

我们取得了一些进展,但这还不够。我们需要将绝对路径添加到 Python 路径中,而不是相对路径。

[main_02] 脚本是 [main_01] 的变体,它使用了一个配置文件 [config.json]


{
  "dependencies": [
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir1",
    "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/dir2"
  ]
}

[dependencies] 键的值是需要添加到 Python 路径中的目录列表。请注意,这里我们使用的是绝对路径,而不是相对路径。

[main_02] 脚本使用 [config.json] 文件的方式如下:

import codecs
import json
import sys

#  configuration file
config_filename="C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/05/config.json"
#  reading the json configuration file
with codecs.open(config_filename, "r", "utf-8") as file:
    config = json.load(file)

#  modification of sys.path
for directory in config['dependencies']:
    sys.path.append(directory)

#  import module4
from module4 import f4
#  execution f4
f4()
  • 第 6 行:请注意,我们使用了配置文件的绝对路径;
  • 第 8–9 行:读取配置文件。根据其内容构建一个字典 [config](第 9 行);
  • 第 11–13 行:数组 [config['dependencies']] 的元素被添加到 Python 路径中。请注意,由于我们在 [config.json] 中使用了绝对文件夹名称,因此我们向 Python 路径中添加的也是绝对路径;
  • 第 16 行:导入 [module4]。由于 [dir2] 现已加入 Python 路径,因此应能找到该模块;

执行结果与 [main_02] 相同,唯一区别在于即使执行目录不再是 [import/05],脚本仍会继续运行:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 05/main_02.py
f3
f4

第 1 行,执行目录为 [import]

我们取得了进展。我们已经看到:

  • 我们必须自己构建 Python 路径;
  • 我们必须包含应用程序所导入模块所在的所有文件夹的绝对路径;

然而,在脚本中写入绝对路径并不是解决办法。一旦项目被移至其他位置,它便无法正常运行。我们需要另寻他法。

9.6. 脚本 [import_06]

Image

注意:文件夹 [06, dir1, dir2] 已放置在 PyCharm 项目的 [Root Sources] 目录下。文件夹 [dir1, dir2] 与前面的示例中的完全相同。

[config.json] 文件内容如下:


{
  "rootDir": "C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06",
  "relativeDependencies": [
    "dir1",
    "dir2"
  ],
  "absoluteDependencies": [
   ]
}

我们将引入两种类型的路径:

  • 绝对路径,第 7–8 行;
  • 相对路径,第3–6行。这些路径相对于第2行的根目录。因此,当项目移动到新位置时,只需修改这一行;

[utils.py] 脚本使用 [config.json] 文件并构建 Python 路径:

#  imports
import codecs
import json
import os
import sys

#  application configuration
def config_app(config_filename: str) -> dict:
    #  config_filename: name of configuration file
    #  we let the exceptions rise

    #  using the configuration file
    with codecs.open(config_filename, "r", "utf-8") as file:
        config = json.load(file)
    #  add dependencies to sys.path
    rootDir = config['rootDir']
    #  add the project's relative dependencies to the syspath
    for directory in config['relativeDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, f"{rootDir}/{directory}")
    #  we add the project's absolute dependencies to the syspath
    for directory in config['absoluteDependencies']:
        #  add the dependency at the beginning of the syspath
        sys.path.insert(0, directory)
    #  return the configuration dictionary
    return config

#  executed script file
def get_scriptdir():
    return os.path.dirname(os.path.abspath(__file__))
  • 第 8 行:[config_app] 函数将配置文件的名称作为参数接收;
  • 第 12–14 行:使用配置文件创建 [config] 字典;
  • 第 20 行:[sys.path] 是 Python 路径中的目录列表;
  • 第 17–20 行:将配置文件中的相关依赖项添加到 Python 路径中。这些依赖项被添加到 [sys.path] 列表的开头(第 20 行)。这是因为当 Python 搜索模块时,会按顺序遍历 [sys.path] 中的目录。 然而,在本文档中,同名的模块将位于 [sys.path] 内的不同目录中。通过将应用程序的依赖项放置在 [sys.path] 数组的首位,我们确保在搜索 [sys.path] 中其他可能包含同名模块的目录之前,先搜索这些依赖项;
  • 第 21–24 行:将配置文件的绝对依赖项添加到 Python 路径中;
  • 第 26 行:返回应用程序配置;
  • 第 29–30 行:[get_scriptdir] 函数返回包含当前正在运行的脚本(即包含该函数调用的脚本)的目录的绝对路径;

主脚本 [main] 如下所示:

#  imports
import sys

from utils import config_app


def affiche_path(msg: str):
    #  message
    print(f"{msg}------------------------------")
    #  sys.path
    for path in sys.path:
        print(path)


#  hand -------------
try:
    #  the sys.path is configured
    affiche_path("avant....")
    config = config_app(f"{get_scriptdir()}/config.json")
    affiche_path("après....")
    #  import module4
    from module4 import f4
    #  execution f4
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • 第 4 行:导入了 [config_app] 函数。请注意,由于 [utils] [main] 位于同一目录下,因此此 [import] 语句始终有效。这是因为主脚本所在的目录会被自动添加到 Python 路径中;
  • 第 7–12 行:[display_path] 函数显示 Python 路径中的文件夹列表;
  • 第 19 行:配置应用程序。请注意,配置文件的绝对路径被传递给了 [config_app] 函数。执行此语句后,Python 路径已被重建;
  • 第 22 行:我们导入 [module4]。得益于 Python 路径的重建,该模块将被成功找到;
  • 第 24 行:执行 [f4] 函数;

在 PyCharm 环境中,执行结果如下:


C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared

C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\fonctions\shared
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\v01\shared
….
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done
 
Process finished with exit code 0

注释

  • 第 2–13 行:PyCharm 的 Python 路径。它包含放置在项目 [Root Sources] 中的所有文件夹;
  • 第 14–29 行:由 [config_app] 函数构建的 Python 路径。第 15–16 行包含我们添加的两个依赖项;
  • 第 22–27 行:执行该脚本的 Python 解释器的系统目录;
  • 第 28–29 行:执行正常进行;

现在,让我们回到之前引发运行时错误的上下文:

  • 打开一个 Python 终端;
  • 切换到不包含该脚本的目录;

(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 06/main.py
avant....------------------------------
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
après....------------------------------
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir2
C:/Data/st-2020/dev/python/cours-2020/v-02/imports/06/dir1
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import\06
C:\Program Files\Python38\python38.zip
C:\Program Files\Python38\DLLs
C:\Program Files\Python38\lib
C:\Program Files\Python38
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\lib\site-packages
f3
f4
done

这次运行成功了(第20–21行)。请注意,[sys.path] 包含的目录与在 PyCharm 中运行时并不相同。

9.7. 脚本 [import_07]

我们通过以下两种方式改进了之前的解决方案:

  • 我们将配置文件 [config.json] 替换为脚本 [config.py]。事实上,JSON 文件存在一个显著问题:无法添加注释。[config.json] 字典可以替换为 Python 字典,后者具有可添加注释的优势;
  • 我们使用一个对机器上所有 Python 项目都可见的模块;

9.7.1. 安装全局模块

Image

在上文中,我们在 PyCharm 项目中创建了一个名为 [packages/myutils] 的文件夹(名称不重要)。

[myutils.py] 脚本内容如下:

#  imports
import sys
import os


def set_syspath(absolute_dependencies: list):
    #  absolute_dependencies: a list of absolute folder names

    #  add the project's absolute dependencies to the syspath
    for directory in absolute_dependencies:
        #  we check the existence of the file
        existe = os.path.exists(directory) and os.path.isdir(directory)
        if not existe:
            #  an exception is lifted
            raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
        else:
            #  add the folder at the beginning of the syspath
            sys.path.insert(0, directory)
  • 第 6–18 行:[set_syspath] 函数使用作为参数传递的目录列表创建一个 Python 路径;
  • 第 12–15 行:我们检查要添加到 Python 路径中的目录是否存在;

[__init.py__] 脚本(名称前后各有一个下划线;这是必需的)如下:


from .myutils import set_syspath

我们从 [myutils] 脚本中导入 [set_syspath] 函数。表示法 [.myutils] 指代路径 [./myutils],这意味着 [myutils] 脚本位于与 [__init.py] 相同的目录下。 我们本可以使用 [myutils] 这种写法。但是,我们将创建一个全局 [myutils] 模块。因此,[from myutils import set_syspath] 这种写法会变得模棱两可。它是指从当前目录导入 [myutils] 脚本,还是指导入全局 [myutils] 脚本?[.myutils] 这种写法解决了这种歧义。

[setup.py] 脚本(此处的名称也是固定的)如下所示:


from setuptools import setup
 
setup(name='myutils',
      version='0.1',
      description='Utilitaire fixant le Python Path',
      url='#',
      author='st',
      author_email='st@gmail.com',
      license='MIT',
      packages=['myutils'],
      zip_safe=False)

在此脚本中,我们描述了即将创建的模块。这里我们将本地创建该模块。不过,创建官方发布的模块(参见 |pypi|)也采用相同的流程。此处的关键要点如下:

  • 第 3 行:正在创建的模块名称;
  • 第 4 行:模块的版本号;
  • 第 5 行:模块的描述;
  • 第 7–8 行:模块的作者;

若要以全机范围安装此模块,请按以下步骤操作:

Image

然后,在 Python 终端中输入以下命令 [pip install .]


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install .
Processing c:\data\st-2020\dev\python\cours-2020\python3-flask-2020\packages
Using legacy setup.py install for myutils, since package 'wheel' is not installed.
Installing collected packages: myutils
  Attempting uninstall: myutils
    Found existing installation: myutils 0.1
    Uninstalling myutils-0.1:
      Successfully uninstalled myutils-0.1
    Running setup.py install for myutils ... done
Successfully installed myutils-0.1

从现在起,该机器上的任何脚本都可以导入 [myutils] 模块,而无需将其包含在项目代码中。

9.7.2. [config.py] 脚本

Image

[config.py] 脚本负责处理应用程序的配置:

def configure():
    import os

    #  absolute name of the configuration script folder
    script_dir = os.path.dirname(os.path.abspath(__file__))
    #  absolute paths of folders to put in the syspath
    absolute_dependencies = [
        #  local files
        f"{script_dir}/dir1",
        f"{script_dir}/dir2",
    ]
    #  update syspath
    from myutils import set_syspath
    set_syspath(absolute_dependencies)

    #  return the config
    return {}
  • 第 1 行:[configure] 函数负责处理应用程序配置;
  • 第 7–10 行:之前在 [config.json] 中的字典;
  • 第 9–10 行:由于我们处于脚本中,可以直接访问绝对文件夹名称 [dir1, dir2]
  • 第 12–14 行:我们使用刚刚创建的 [myutils] 模块中的 [set_syspath] 函数来设置配置的 Python 路径;
  • 第 20 行:返回应用程序配置字典。此处,该字典为空;

9.7.3. [main.py] 脚本

主脚本 [main] 如下所示:

#  configure the application
import config

config = config.configure()

#  syspath is configured - imports can be made
from module4 import f4

#  hand -------------
try:
    f4()
except BaseException as erreur:
    print(f"L'erreur suivante s'est produite : {erreur}")
finally:
    print("done")
  • 第 2–4 行:我们使用 [config.py] 模块配置应用程序。由于该模块与主脚本位于同一目录下,因此可以访问它。然而,主脚本所在的目录始终是 Python 路径的一部分;
  • 当执行到第 6 行时,Python 路径已构建完成,并包含了 [module4] 模块所在的文件夹。因此,我们可以在第 7 行导入该模块;
  • 第 10–15 行:剩下的就是调用 [f4] 函数;

在 PyCharm 中的执行结果如下:

1
2
3
4
5
6
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/import/07/main.py
f3
f4
done

Process finished with exit code 0

在主脚本目录外的 Python 终端中,结果如下:

1
2
3
4
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\import>python 07/main.py
f3
f4
done

从现在起,我们将始终按照相同的步骤来配置应用程序:

  • 在主脚本所在的目录中放置一个 [config.py] 脚本。该脚本包含一个 [configure] 函数,其作用有两点:
    • 构建应用程序的 Python 路径。为此,[config.py] 会列出所有包含应用程序所用模块的文件夹,并使用这些模块的绝对路径来构建 Python 路径;
    • 构建应用程序配置的 [config] 字典;

我们将这种方法应用到练习题的第二个版本中。您可能还记得,第一个版本在 PyCharm 环境中可以运行,但在 Python 终端中却无法运行。问题出在 Python 路径上。