西安科技大学

安全工程专业课程

安全仿真与模拟基础


金洪伟 & 闫振国 & 王延平

西安科技大学安全科学与工程学院


返回目录⇡

如何浏览?

  1. 从浏览器地址栏打开 https://zimo.net/aqmn/
  2. 点击章节列表中的任一链接,打开相应的演示文稿;
  3. 点击链接打开演示文稿,使用空格键或方向键导航;
  4. f键进入全屏播放,再按Esc键退出全屏;
  5. Alt键同时点击鼠标左键进行局部缩放;
  6. Esco键进入幻灯片浏览视图。

请使用最新版本浏览器访问此演示文稿以获得更好体验。

第 2 部分  Python 基础

第 7 章  模块

目 录

  1. 模块
  2. 包管理工具

1. 模块

1.1 什么是模块?

在本章以前,我们一直将编写的代码放在一个 .py 源文件文件中,但已经尝试过使用 Python 的内建模块,如 mathrandom。随着代码规模的增大,将不可避免地需要将代码放在多个 .py 源文件中,这能方便对代码的组织管理,这时就要使用模块的概念。

模块(Module)就是以 .py 结尾的 Python 源文件,它包含定义(变量、函数和类)和语句。模块是 Python 代码的组织单元,它实际上也是一种对象。模块对应一个名字空间,其中包含任意的对象。

1.2 创建模块

要创建模块,只需将模块代码保存在文件扩展名为 .py 的文件中。以下是 say_hello.py 文件内容:


            hello_words = {'en-US': 'Hello!', 'zh-CN': '您好!',
                           'zh-TW': '您好!', 'ja-JP': 'こんにちは',
                           'nl-NL': 'Hallo', 'ko-KR': '안녕하세요'}

            def hello(lang='zh-CN'):
                print(hello_words[lang])
        

该文件对应的模块名称为 say_hello。模块命名和变量的命名习惯类似,即采用蛇形命名法。

1.3 导入并使用模块

要在其他文件中使用刚创建的模块,必须先使用 import 语句导入此模块,有多种不同的导入方法。

最常规的导入方式是 import module_name,该语句会执行模块中的语句,进一步可以 module_name.name 的方式引用模块中的名称。如下为与 say_hello.py 同在一个目录下的 main.py 文件的内容:


            import say_hello

            say_hello.hello_words['it-IT'] = 'Ciao'
            say_hello.hello('nl-NL')  # Hallo
            say_hello.hello('it-IT')  # Ciao
        

1.3 导入并使用模块

在导入模块时,还可以在模块名后使用 import module_name as alias 为模块指定别名:


            import say_hello as say

            say.hello_words['it-IT'] = 'Ciao'
            say.hello('nl-NL')  # Hallo
            say.hello('it-IT')  # Ciao
        

1.3 导入并使用模块

可以使用 from module_name import name1, name2 的形式只导入模块中的特定名称到当前命名空间(namespace)。如果要同时导入多个名称,各项之间以逗号分割。如下所示:


            from say_hello import hello_words, hello

            hello_words['it-IT'] = 'Ciao'
            hello('nl-NL')  # Hallo
            hello('it-IT')  # Ciao
        

这时使用这些名称时不需要也不能使用前导的 module_name.,即应该用 hello('nl-NL') 而不是 say_hello.hello('nl-NL')

1.3 导入并使用模块

也可以使用 from module_name import * 的形式直接将模块中所有的公开名称(不以下划线 _ 开头的名称)导入到当前命名空间:


            from say_hello import *

            hello_words['it-IT'] = 'Ciao'
            hello('nl-NL')  # Hallo
            hello('it-IT')  # Ciao
        

但这种方式常常会导入大量的未预料的名称,这些名称甚至会遮蔽已有的名称,因此通常不建议使用

1.3 导入并使用模块

还可以使用 from module_name import name as alias 的形式为特定导入名称指定别名:


            from say_hello import hello as hi

            hi('nl-NL')  # Hallo
        

1.4 模块搜索路径

当导入一个模块 module_name 时,会按如下顺序寻找该模块:

  • 检查该名称是否为内置模块
  • 如果不是内置模块,会在 sys.path 目录列表中查找此模块,其查找顺序为:

    • 输入脚本的目录(或未指定文件时的当前目录);
    • PYTHONPATH 环境变量所列出的目录
    • 依赖于安装的默认值(按照惯例包括一个 site-packages 目录,由 site 模块处理)。

更多信息,请沿着以上链接进一步学习。

1.5 查看模块信息

❶ 使用 dir() 函数

内置的 dir() 函数会返回经过排序的对象属性名称列表,可借此函数查看模块的属性:


            import say_hello

            print(dir())  # 没有实参时,返回当前本地作用域中的名称列表
            # ['__annotations__', '__builtins__', '__cached__',
            # '__doc__', '__file__', '__loader__', '__name__',
            # '__package__', '__spec__', 'say_hello']

            print(dir(say_hello))  # 有实参时,尝试返回该对象的有效属性列表
            # ['__builtins__', '__cached__', '__doc__',
            # '__file__', '__loader__', '__name__',
            # '__package__', '__spec__', 'hello', 'hello_words']
        

❷ 模块属性使用示例

可以进一步使用这些内置的名称,如下所示:


            print(__name__)  # __main__
            print(__package__)  # None

            print(say_hello.__cached__)
            # C:\Users\jin\PycharmProjects\module\__pycache__\say_hello.cpython-310.pyc
            print(say_hello.__doc__)  # None
            print(say_hello.__file__)
            # C:\Users\jin\PycharmProjects\module\say_hello.py
            print(say_hello.__name__)  # say_hello
        

❷ 模块属性使用示例

使用 PyCharm 建立 Python 项目时,其默认生成的 main.py 文件具有以下代码:


            if __name__ == '__main__':
                ...
        

'__main__' 始终是当前执行模块内置变量 __name__ 的值。此 if 语句的条件确保 main.py 文件对应的 main 模块在被导入到其他模块后,其语句体不会被执行(因为被导入后该模块的 __name__ 变量值是 'main' 而不是 '__main__')。这正是我们想要的,因为我们本不想让 main 模块被其他模块导入和执行,这使得该 if 语句的句体看起来像其他语言的 main() 入口函数。

❸ 查看已被加载的模块

sys 是 Python 中一个比较重要的内建模块,它提供一些和系统相关的变量和函数。其中 sys.modules 变量会存储已经加载的模块:


            import say_hello, sys

            print(sys.modules)
            # {'sys': <module 'sys' (built-in)>,
            # 'builtins': <module 'builtins' (built-in)>,
            # ...
            # 'say_hello': <module 'say_hello' from 'C:\\Users\\jin\\PycharmProjects\\module\\say_hello.py'>}
        

可以看出 sys.modules 是字典类型,它将模块名称映射到已经被加载的模块。可借此查看某一模块的信息:


            print(sys.modules['say_hello'])
            # <module 'say_hello' from 'C:\\Users\\jin\\PycharmProjects\\module\\say_hello.py'>
        

2. 包

2.1 什么是包?

一个稍微有点规模的项目,其模块将不止一个,需要某种方法将这些相互关联的模块统一进行管理。对应于使用目录来管理文件,同样可以将多个模块文件所处的目录定义为一个新的东西:包,为了明确标示某个目录是一个包,需要在目录内放入一个 __init__.py 文件。

因此,简单来说,(Package)是一个包含 __init__.py 文件的目录,是目录内模块的集合。包本身也是一个模块,相对来说,包内的模块又称为子模块(Submodule)。

2.2 包的目录结构

如下面的目录结构中,my_pkg 目录对应一个包,而其中的 foo.pybar.py 都对应一个子模块。


            my_pkg/
            ├── __init__.py
            ├── bar.py
            └── foo.py
        

注意包目录下必须有一个 __init__.py 文件,否则 Python 会把该目录当成普通目录而不是包了。最简单的情况下,该文件是一个空文件。该包的名称是 my_pkg(注意不是 __init__)。

2.2 包的目录结构

进一步地,可以有多级目录,组成多级层次的包结构,其中的子目录也可以被称作是子包(Subpackage)。如下所示:


            my_pkg/
            ├── __init__.py
            ├── bar.py
            ├── foo.py
            ├── sub_pkg1
            │   ├── __init__.py
            │   ├── bar.py
            │   └── foo.py
            └── sub_pkg2
                ├── __init__.py
                ├── bar.py
                └── foo.py
        

以上的包 my_pkg 中又包含两个子包:sub_pkg1sub_pkg2

2.4 导入包中的名称

当存在多层的树形结构后,就需要用多个点号来逐层表示包中的一些名称,如 my_pkg.sub_pkg2.foo.name,具体使用方法取决于如何导入这些名称。外部代码可以通过如下方式导入包内定义的名称(其中方括号内的内容是可选的):


            import package_name[.module_name [as alias]]
            from package_name import module_name [as alias]
            from package_name.module_name import name [as alias]
        

总之,包、包中模块、模块中的名称的导入方法基本上是一致的,可以灵活地导入这些名称,并为导入的名称指定别名。

2.4 导入包中的名称

为了便于说明如何导入包,假设我们项目的目录结构为:


            my_project
            ├── main.py
            └── my_pkg
                ├── __init__.py
                ├── bar.py
                ├── foo.py
                ├── sub_pkg1
                │   ├── __init__.py
                │   ├── bar.py
                │   └── foo.py
                └── sub_pkg2
                    ├── __init__.py
                    ├── bar.py
                    └── foo.py
        

main.py 对应项目的主模块。

2.4 导入包中的名称

当使用 import package_name.module_name 的形式导入包时,引用时必须使用模块的全名。假如 my_pkg.sub_pkg1.foo 模块中有一个函数 do_sth(),则在 my_project/main.py 中可以按如下方法导入模块并使用此函数:


            import my_pkg.sub_pkg1.foo
            my_pkg.sub_pkg1.foo.do_sth()  # 不能使用 foo.do_sth() 的形式
        

但当以 from package_name import module_name 形式导入时,引用时不必使用全名。如下所示:


            from my_pkg.sub_pkg1 import foo
            foo.do_sth()  # 不能使用 my_pkg.sub_pkg1.foo.do_sth() 的形式
        

2.5 编辑 __init__.py 文件

前面的各种包的 __init__.py 都是空文件。实际上,可以在该文件中放入包的初始化代码,当包本身或其所包含内容被导入时会执行其中的代码。不过一般会在该文件中定义一个 __all__ 变量,它是一个字符串列表,列表中的每个元素是该包中包含的成员(模块、子包)名称。这样当在其他文件中用 from package_name import * 的形式导入该包的内容时,将只导入该列表中的成员。例如,假如 my_pkg/__init__.py 的内容如下:


            __all__ = ['foo', 'bar']
        

则可以如下方式编写 my_project/main.py


            from my_pkg import *
            foo.do_sth()  # 在上一行通过 * 导入的名称中已包含 foo
        

2.5 编辑 __init__.py 文件

如果不想让他人以 from package_name import * 的形式导入包中特定的子模块,保持 __init__.py 为空文件即可。

实际上,也可以为其他不属于包的模块显式指定 __all__ 变量,其列表中各项可以是变量、函数、类名称等定义项,从而控制以 from module_name import * 形式导入时具体导入的内容。

3. 包管理工具

一个 Python 程序往往会使用别人已经编写好的代码库,这些代码库中包含包、模块和资源文件,并以某种机制打包、标注版本并发布到特定的软件仓库注册服务中心,以供人们下载和使用,这些代码库被称为发布包Distribution Package),简称包(注意这里的包和上一节中的包并不是一回事)。

Python 有较多的软件仓库注册服务中心,如 PyPIconda-forge,另外也有许多不同的包管理工具(或称依赖管理工具),如 pipconda。PyPI(Python Package Index)是 Python 官方支持的软件仓库,相应地 pip 是 Python 默认安装和使用的包管理工具。本节将主要介绍如何利用 pip 从 PyPI 中下载和使用由 Python 社区开发和共享的发布包。

3. 包管理工具

当我们以从 Python 官网下载安装包的方式安装 Python 后,默认也安装了 pip 工具,我们可以在命令行中通过如下两种方式验证 pip 是否安装,并查看其版本:


            $ python -m pip --version
            pip 23.2.1 from C:\Program Files\Python312\Lib\site-packages\pip (python 3.12)
        

            $ pip --version
            pip 23.2.1 from C:\Program Files\Python312\Lib\site-packages\pip (python 3.12)
        

第一个 python -m pip 命令使用 python 可执行文件将 pip 模块作为脚本运行,-m 选项告诉 Python 运行指定的模块作为脚本。第二个 pip 则使用 PATH 环境变量中的默认 Python 解释器执行 pip 模块,大多数情况下,两者的效果是一样的。

3. 包管理工具

让我们从命令行输入 pippip helppip --help,以查看该命令的简明帮助信息:


$ pip

Usage:
    pip <command> [options]

Commands:
    install                     Install packages.
    download                    Download packages.
    uninstall                   Uninstall packages.
    freeze                      Output installed packages in requirements format.
    inspect                     Inspect the python environment.
    list                        List installed packages.
    show                        Show information about installed packages.
    check                       Verify installed packages have compatible dependencies.
    config                      Manage local and global configuration.
    search                      Search PyPI for packages.
    cache                       Inspect and manage pip's wheel cache.
    index                       Inspect information available from package indexes.
    wheel                       Build wheels from your requirements.
    hash                        Compute hashes of package archives.
    completion                  A helper command used for command completion.
    debug                       Show information useful for debugging.
    help                        Show help for commands.

General Options:
    -h, --help                  Show help.
    --debug                     Let unhandled exceptions propagate outside the main subroutine, instead of logging them to stderr.
    --isolated                  Run pip in an isolated mode, ignoring environment variables and user configuration.
    --require-virtualenv        Allow pip to only run in a virtual environment; exit with an error otherwise.
    --python <python>           Run pip with the specified Python interpreter.
    -v, --verbose               Give more output. Option is additive, and can be used up to 3 times.
    -V, --version               Show version and exit.
    -q, --quiet                 Give less output. Option is additive, and can be used up to 3 times (corresponding to WARNING, ERROR, and CRITICAL logging levels).
    --log <path>                Path to a verbose appending log.  
        

3. 包管理工具

对于常使用命令行的人来说,以上信息已经一目了然了,尤其是其中的各个命令(Commands)明确列出了使用 pip 可以进行的各项操作。让我们依照这些命令,安装著名的科学计算软件包 numpy

$ pip install numpy
Collecting numpy
    Downloading numpy-1.26.2-cp312-cp312-win_amd64.whl.metadata (61 kB)
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.2/61.2 kB 155.4 kB/s eta 0:00:00
Downloading numpy-1.26.2-cp312-cp312-win_amd64.whl (15.5 MB)
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.5/15.5 MB 1.4 MB/s eta 0:00:00
Installing collected packages: numpy
Successfully installed numpy-1.26.2

3. 包管理工具

很可能你的安装过程和上述不一样,比如对于 Windows 系统,可能出现如下结果:

$ pip install numpy
Defaulting to user installation because normal site-packages is not writeable
Collecting numpy
    Downloading numpy-1.26.2-cp312-cp312-win_amd64.whl.metadata (61 kB)
        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.2/61.2 kB 181.4 kB/s eta 0:00:00
Downloading numpy-1.26.2-cp312-cp312-win_amd64.whl (15.5 MB)
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 15.5/15.5 MB 434.9 kB/s eta 0:00:00
Installing collected packages: numpy
    WARNING: The script f2py.exe is installed in 'C:\Users\jin\AppData\Roaming\Python\Python312\Scripts' which is not on PATH.
    Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed numpy-1.26.2

警告信息很可能是因为你在安装 Python 时使用管理员权限为所有用户安装(默认安装到 C:\Program Files\Python312 目录),同时自动将 C:\Program Files\Python312\Scripts\ 目录添加到 PATH 环境变量中;但在使用 pip 命令时,因不具备管理员权限,只能将包安装在用户脚本目录中,而该目录并没有被添加到 PATH 中,可能导致安装的程序没法被找到。对于这种情况,一般使用管理员权限启动命令提示符;也可以自行将警告信息中提示的用户脚本目录添加到 PATH 中。

3. 包管理工具

另外,你在安装包时很可能遇到网络超时错误,这是因为 PyPI 服务器在国外,从国内访问的速度一般很低,这时可以设置使 pip 使用国内镜像源。国内有很多 PyPI 镜像源:

  • 清华大学:https://pypi.tuna.tsinghua.edu.cn/simple/
  • 上海交通大学:https://mirror.sjtu.edu.cn/pypi/web/simple/
  • 阿里云:https://mirrors.aliyun.com/pypi/simple/
  • 腾讯云:https://mirrors.cloud.tencent.com/pypi/simple/
  • 华为云:https://mirrors.huaweicloud.com/repository/pypi/simple
  • 网易:https://mirrors.163.com/pypi/simple/
  • 百度:https://mirror.baidu.com/pypi/simple/
  • ……

3. 包管理工具

要临时使用某个镜像源安装某个包,可以使用类似如下方法:


            $ pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
        

要将某个源设置为默认源,则可以运行命令:


            $ pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
        

具体的设置方法可以参考 pip 配置清华源使用阿里云源使用腾讯云源使用华为云源使用等文档。

3. 包管理工具

关于 pip 工具使用的更多细节,请参见其官方文档。

另外,请注意,我们这里只是以最简单的方式安装 Python 软件包,还有其他更灵活、更强大但也更复杂的方法,我们也可以把自己编写的 Python 软件在 PyPI 上开源发布,所有这一切,请参见官方的 Python 打包指南文档。

作业

作业

要求:

  1. 本章作业可能包含多个 .py 源文件,请将这些源文件放在名称为 安模作业02-07-学号-姓名 的目录中,并将该目录打包为 ch07-学号-姓名.zip 形式的压缩文件,通过电子邮件以附件形式发给任课教师。
  2. 在源文件中以注释的形式醒目地写明本次作业对应的章编号、各个作业题的编号,并按要求写出解题思路、代码注释。
  3. 以上各题不能只有文字说明,而应同时有可执行的示例代码。
  4. 邮件标题统一用 安模作业02-07-学号-姓名 的形式。
  5. 所有关于作业的回答都以代码注释的形式写在源文件中,不需要再额外附加其他文档或图片,请保证代码执行不会发生错误。
  6. 待本次作业批改后,请通过此链接下载本次作业的参考答案。

  谢谢!

返回目录
返回首页