模块#

模块是包含 Python 定义和语句的文件。其文件名是模块名加后缀名 .py 。在模块内部,通过全局变量 name 可以获取模块名(即字符串)。

import os
import sys
module_path = os.path.abspath(os.path.join('.')) # 将当前jupyter目录加入到系统路径下面
if module_path not in sys.path:
    sys.path.append(module_path)
import fibo
dir(fibo)
['__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'fib',
 'fib2']
fibo.__name__
'fibo'
!cat fibo.py
# -*- coding: utf-8 -*-

# 斐波那契数列模块

def fib(n):    # 打印斐波那契数列直到 n
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # 返回斐波那契数列直到 n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

if __name__ == "__main__":
    print(fib(100))
!python3 fibo.py
0 1 1 2 3 5 8 13 21 34 55 89 
None

导入包#

模块包含可执行语句及函数定义。这些语句用于初始化模块,且仅在 import 语句 第一次 遇到模块名时执行(文件作为脚本运行时,也会执行这些语句)。

每个模块都有自己的私有命名空间,它会被用作模块中定义的所有函数的全局命名空间。 我们可以通过与引用模块函数一样的标记法 modname.itemname 来访问一个模块的全局变量。

模块可以导入其他模块。 根据惯例可以将所有 import 语句都放在模块(或者也可以说是脚本)的开头但这并非强制要求。 如果被放置于一个模块的最高层级,则被导入的模块名称会被添加到该模块的全局命名空间。

import fibo
fibo.fib(1000) # 使用包里面函数

from fibo import fib, fib2 # 显示指定导入包里面的名称

from fibo import * # 导入所有不以下划线(_)开头的名称,不推荐,因为可能会覆盖已经定义的名称

import fibo as fib # 使用模块别名

from fibo import fib as fibonacci # 使用模块内部名称的别名
fibonacci(500)

import importlib # 默认只导入一次模块。如果更改了模块内容,可以重新加载模块
importlib.reload(modulename) # 重新加载模块名

以脚本方式执行模块#

可以用以下方式运行 Python 模块:

python fibo.py <arguments>

这项操作将执行模块里的代码,和导入模块一样,但会把 name 赋值为 “main”。 也就是把下列代码添加到模块末尾:

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

模块搜索路径#

当导入一个名为 spam 的模块时,会安装如下流程搜索:

  1. 解释器首先会搜索具有该名称的内置模块(所有内置模块见:sys.builtin_module_names )

  2. 如果未找到,它将在变量 sys.path 所给出的目录列表中搜索名为 spam.py 的文件,sys.path初始化值来自:

    1. 被命令行直接运行的脚本所在的目录(或未指定文件时的当前目录)。

    2. PYTHONPATH (目录列表,与 shell 变量 PATH 的语法一样)。

    3. 依赖于安装的默认值(按照惯例包括一个 site-packages 目录,由 site 模块处理)。

注意:

  1. 在支持符号链接的文件系统中,“被命令行直接运行的脚本所在的目录”是符号链接最终指向的目录。换句话说,符号链接所在的目录并 没有 被添加至模块搜索路径。

  2. 初始化后,Python 程序可以更改 sys.path。这样可以添加自定义的目录到搜索路径中:

    import sys
    sys.path.append('/ufs/guido/lib/python')
    

“已编译的” Python 文件#

为了快速加载模块,Python 把模块的编译版本缓存在 pycache 目录中,文件名为 module.version.pyc,version 对编译文件格式进行编码,一般是 Python 的版本号。例如,CPython 的 3.3 发行版中,spam.py 的编译版本缓存为 pycache/spam.cpython-33.pyc。这种命名惯例让不同 Python 版本编译的模块可以共存。

Python 对比编译版与源码的修改日期,查看编译版是否已过期,是否要重新编译。此进程完全是自动的。此外,编译模块与平台无关,因此,可在不同架构的系统之间共享相同的库。

Python 在两种情况下不检查缓存。一,从命令行直接载入的模块,每次都会重新编译,且不储存编译结果;二,没有源模块,就不会检查缓存。为了让一个库能以隐藏源代码的形式分发(通过将所有源代码变为编译后的版本),编译后的模块必须放在源目录而非缓存目录中,并且源目录绝不能包含同名的未编译的源模块。

标准模块#

Python 的“标准模块”= 标准库(Standard Library)里所有随解释器一起发布、无需额外安装的模块。Python 3.11 版源码树中,标准库共 200+ 个包/模块,覆盖日常几乎所有需求:文件、网络、系统、并发、算法、测试、调试……

变量 sys.path 是字符串列表,用于确定解释器的模块搜索路径。该变量以环境变量 PYTHONPATH 提取的默认路径进行初始化,如未设置 PYTHONPATH,则使用内置的默认路径。可以用标准列表操作修改该变量:

import sys
sys.path.append('/ufs/guido/lib/python')

下面是标准库的“速查地图”:

>>> import sys
>>> sys.stdlib_module_names   # Python 3.10+ 官方集合
>>> help('modules')           # 交互式列出全部
>>> python -m pydoc -b        # 一键开浏览器看标准库文档

操作系统 & 文件系统#

模块

一句话用途

os

跨进程、文件/目录、环境变量、路径拼接

pathlib

面向对象的文件系统路径(Python 3.4+)

shutil

高级文件操作:复制、移动、递归删除

glob / fnmatch

通配符匹配文件

tempfile

安全临时文件/目录

文件格式 & 数据交换#

模块

一句话用途

json

JSON ↔ Python

csv

读写 CSV

sqlite3

内置 SQLite 数据库

configparser

INI 配置文件

zipfile / tarfile

压缩包读写

pickle / shelve

对象序列化/持久化

时间与日期#

模块

一句话用途

datetime

日期、时间、时差、格式化

time

时间戳、睡眠、计时器

calendar

日历、闰年判断

进程 / 线程 / 并发#

模块

一句话用途

subprocess

生成新进程、管道通信

threading

线程、锁、事件

multiprocessing

绕过 GIL 的多进程、进程池

concurrent.futures

高层线程/进程池(ThreadPoolExecutor / ProcessPoolExecutor

asyncio

原生异步协程框架

queue

线程/进程安全队列

网络 & 互联网#

模块

一句话用途

urllib.request

打开 URL、HTTP 请求

urllib.parse

URL 拆分/组装/转义

http.server

一键静态文件 HTTP 服务器

ftplib / smtplib / poplib / imaplib

FTP、发邮件、收邮件

socket

底层 TCP/UDP 编程

ssl

TLS/SSL 封装

json + urllib ≈ 极简 REST 客户端

数据类型 & 算法#

模块

一句话用途

collections

高性能扩展容器:dequeCounterdefaultdictOrderedDict

heapq

堆/优先队列

bisect

已排序列表二分插入/查找

array

紧凑数值数组

enum

枚举类型

dataclasses

自动生成特殊方法的“数据类”(3.7+)

typing

类型注解支持

数学 & 随机#

模块

一句话用途

math

浮点数学函数

cmath

复数数学函数

decimal

任意精度十进制(财务计算)

fractions

有理数

random

伪随机数、随机抽样

statistics

均值、中位数、方差等统计函数

调试 / 测试 / 性能#

模块

一句话用途

logging

标准日志系统(线程安全、分级、Handler)

unittest

单元测试框架(xUnit 风格)

doctest

文档字符串里写测试

pdb

命令行调试器

traceback

打印/格式化异常栈

cProfile / profile / timeit

性能剖析、计时

tracemalloc

追踪内存分配

语言服务 & 运行时#

模块

一句话用途

sys

解释器交互:路径、参数、退出、标准流

warnings

发出/过滤警告

gc

与垃圾回收器交互

weakref

弱引用

importlib

动态导入、重载模块

keyword / tokenize / ast

扫描/解析 Python 源码

更多#

  • itertools 无限迭代器、排列组合、笛卡尔积

  • functools lru_cachepartial、函数式工具

  • operator 用函数形式代替运算符

  • contextlib 用 @contextmanager 一行写上下文管理器

  • argparse 命令行参数解析

  • pathlib 面向对象路径

dir() 函数#

内置函数 dir() 用于查找模块定义的名称。返回结果是经过排序的字符串列表:

import sys
", ".join(dir(sys))
'__breakpointhook__, __displayhook__, __doc__, __excepthook__, __interactivehook__, __loader__, __name__, __package__, __spec__, __stderr__, __stdin__, __stdout__, __unraisablehook__, _base_executable, _clear_type_cache, _current_frames, _debugmallocstats, _framework, _getframe, _git, _home, _xoptions, abiflags, addaudithook, api_version, argv, audit, base_exec_prefix, base_prefix, breakpointhook, builtin_module_names, byteorder, call_tracing, copyright, displayhook, dont_write_bytecode, exc_info, excepthook, exec_prefix, executable, exit, flags, float_info, float_repr_style, get_asyncgen_hooks, get_coroutine_origin_tracking_depth, getallocatedblocks, getdefaultencoding, getdlopenflags, getfilesystemencodeerrors, getfilesystemencoding, getprofile, getrecursionlimit, getrefcount, getsizeof, getswitchinterval, gettrace, hash_info, hexversion, implementation, int_info, intern, is_finalizing, maxsize, maxunicode, meta_path, modules, path, path_hooks, path_importer_cache, platform, platlibdir, prefix, ps1, ps2, ps3, pycache_prefix, set_asyncgen_hooks, set_coroutine_origin_tracking_depth, setdlopenflags, setprofile, setrecursionlimit, setswitchinterval, settrace, stderr, stdin, stdout, thread_info, unraisablehook, version, version_info, warnoptions'

没有参数时,dir() 列出当前已定义的名称:

", ".join(dir())
'In, Out, _, _3, _4, _7, __, ___, __builtin__, __builtins__, __doc__, __loader__, __name__, __package__, __spec__, _dh, _exit_code, _i, _i1, _i2, _i3, _i4, _i5, _i6, _i7, _i8, _ih, _ii, _iii, _oh, exit, fibo, get_ipython, module_path, open, os, quit, sys'

dir() 不会列出内置函数和变量的名称。这些内容的定义在标准模块 builtins 中:

import builtins

", ".join(dir(builtins))
'ArithmeticError, AssertionError, AttributeError, BaseException, BlockingIOError, BrokenPipeError, BufferError, BytesWarning, ChildProcessError, ConnectionAbortedError, ConnectionError, ConnectionRefusedError, ConnectionResetError, DeprecationWarning, EOFError, Ellipsis, EnvironmentError, Exception, False, FileExistsError, FileNotFoundError, FloatingPointError, FutureWarning, GeneratorExit, IOError, ImportError, ImportWarning, IndentationError, IndexError, InterruptedError, IsADirectoryError, KeyError, KeyboardInterrupt, LookupError, MemoryError, ModuleNotFoundError, NameError, None, NotADirectoryError, NotImplemented, NotImplementedError, OSError, OverflowError, PendingDeprecationWarning, PermissionError, ProcessLookupError, RecursionError, ReferenceError, ResourceWarning, RuntimeError, RuntimeWarning, StopAsyncIteration, StopIteration, SyntaxError, SyntaxWarning, SystemError, SystemExit, TabError, TimeoutError, True, TypeError, UnboundLocalError, UnicodeDecodeError, UnicodeEncodeError, UnicodeError, UnicodeTranslateError, UnicodeWarning, UserWarning, ValueError, Warning, ZeroDivisionError, __IPYTHON__, __build_class__, __debug__, __doc__, __import__, __loader__, __name__, __package__, __spec__, abs, all, any, ascii, bin, bool, breakpoint, bytearray, bytes, callable, chr, classmethod, compile, complex, copyright, credits, delattr, dict, dir, display, divmod, enumerate, eval, exec, execfile, filter, float, format, frozenset, get_ipython, getattr, globals, hasattr, hash, help, hex, id, input, int, isinstance, issubclass, iter, len, license, list, locals, map, max, memoryview, min, next, object, oct, open, ord, pow, print, property, range, repr, reversed, round, runfile, set, setattr, slice, sorted, staticmethod, str, sum, super, tuple, type, vars, zip'

内置模块名称保存到sys.builtin_module_names

import sys

", ".join(sys.builtin_module_names)
'_abc, _ast, _codecs, _collections, _functools, _imp, _io, _locale, _operator, _peg_parser, _signal, _sre, _stat, _string, _symtable, _thread, _tracemalloc, _warnings, _weakref, atexit, builtins, errno, faulthandler, gc, itertools, marshal, posix, pwd, sys, time, xxsubtype'

#

模块(module)是单个文件;包(package)是含 init.py 的目录,用来组织多个模块。

模块与包区别#

模块(module)#

  • 本质:一个 .py 文件

  • import foo 时,Python 把文件 foo.py 编译成模块对象,名字就是文件名

  • 例子:

    utils.py   →  import utils
    

包(package)#

  • 本质:一个带 __init__.py 的目录(Python 3.3+ 允许隐式命名空间包,但常规仍建议保留该文件)。

  • 目录名即包名,可嵌套子包;__init__.py 在包首次被导入时执行,用于初始化、暴露接口。

  • 例子:

    graphics/
        __init__.py
        formats/
            __init__.py
            png.py
            jpg.py
        filters/
            __init__.py
            blur.py
    

    使用:

    from graphics.formats import png
    

包是通过使用“带点号模块名”来构造 Python 模块命名空间的一种方式。 例如,模块名 A.B 表示名为 A 的包中名为 B 的子模块。

示例包结构如下:

sound/                          最高层级的包
      __init__.py               初始化 sound 
      formats/                  用于文件格式转换的子包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  用于音效的子包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  用于过滤器的子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

需要有 init.py 文件才能让 Python 将包含该文件的目录当作包来处理(除非使用 namespace package,这是一个相对高级的特性)。 这可以防止重名的目录如 string 在无意中屏蔽后继出现在模块搜索路径中的有效模块。 在最简单的情况下,init.py 可以只是一个空文件,但它也可以执行包的初始化代码或设置 all 变量。

导入模块#

导入单个模块:


import sound.effects.echo # 导入单个模块

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4) # 导入单个模块时候,必须通过其全名来引用

另外一种导入单个模块方法:

from sound.effects import echo
echo.echofilter(input, output, delay=0.7, atten=4) # 引用函数时候不需要再不必加包前缀

直接导入所需的函数或变量:

from sound.effects.echo import echofilter
echofilter(input, output, delay=0.7, atten=4)

注意:

  • 使用 from package import item 时,item 可以是包的子模块(或子包),也可以是包中定义的函数、类或变量等其他名称。import 语句首先测试包中是否定义了 item;如果未在包中定义,则假定 item 是模块,并尝试加载。如果找不到 item,则触发 ImportError 异常。

  • 相反,使用 import item.subitem.subsubitem 句法时,除最后一项外,每个 item 都必须是包;最后一项可以是模块或包,但不能是上一项中定义的类、函数或变量。

从包中导入 *#

使用 from sound.effects import * 时会发生什么?你可能希望它会查找并导入包的所有子模块,但事实并非如此。因为这将花费很长的时间,并且可能会产生你不想要的副作用,如果这种副作用被你设计为只有在导入某个特定的子模块时才应该发生。

唯一的解决办法是提供包的显式索引。import 语句使用如下惯例:如果包的 init.py 代码定义了列表 all,运行 from package import * 时,它就是被导入的模块名列表。发布包的新版本时,包的作者应更新此列表。如果包的作者认为没有必要在包中执行导入 * 操作,也可以不提供此列表。例如,sound/effects/init.py 文件可以包含以下代码:

__all__ = ["echo", "surround", "reverse"]

这意味着 from sound.effects import * 将导入 sound.effects 包的三个命名子模块。

相对导入#

当包由多个子包构成(如示例中的 sound 包)时,可以使用绝对导入来引用同级包的子模块。 例如,如果 sound.filters.vocoder 模块需要使用 sound.effects 包中的 echo 模块,它可以使用 from sound.effects import echo。

可以编写相对导入代码,即使用 from module import name 形式的 import 语句。 这些导入使用前导点号来表示相对导入所涉及的当前包和上级包。 例如对于 surround 模块,可以使用:

from . import echo
from .. import formats
from ..filters import equalizer

注意: 相对导入基于当前模块名。因为主模块名永远是 “main” ,所以如果计划将一个模块用作 Python 应用程序的主模块,那么该模块内的导入语句必须始终使用绝对导入。

为什么在主模块中使用绝对导入?

  • 避免歧义:如果你使用相对导入,如from .subpackage import module2,当模块作为脚本直接运行时,相对导入可能会失败,因为相对导入是相对于当前模块的,而主模块__main__没有父模块。

  • 明确性:绝对导入清楚地表明了模块的来源,使得代码更容易理解和维护。

  • 可移植性:当你的代码被重构或者模块被移动到不同的包中时,绝对导入可以确保导入语句仍然有效。

多目录中的包#

包还支持一个特殊属性 __path__ ,可用于扩展包中的模块集。。在包的 __init__.py 中的代码被执行前,该属性被初始化为一个只含一项的列表,该项是一个字符串,是 __init__.py 所在目录的名称。可以修改此变量;这样做会改变在此包中搜索模块和子包的方式。

资料#