W
e
l
c
o
m
e
: )

11.模块 (module)

本篇导航

本篇讲清楚 Python 的「代码组织三级结构」——模块 → 包 → 库:
§1 模块化的设计理念(为什么需要模块、API 与文档字符串、__name__ 与测试代码)→ §2 模块的五种导入方式(import / from … import / 区别 / __import__() 动态导入 / 只导入一次与 reload)→ §3 包的 __init__.py 本质、__all__、相对导入与 sys.path 搜索路径 → §4 模块的本地发布与安装、上传 PyPI → §5 库与第三方扩展库安装。

阅读建议:§2.3 两种导入方式的区别、§3.3「导入包本质是执行 __init__.py」是理解包机制的关键;§4 的 setup.py 与 §5 的 pip 属实战高频,但原文部分写法已过时(见 §4.1、§4.3 勘误)。

原文勘误(全篇文字性笔误汇总,已直接修正)

原文由 PDF/截图 OCR 转写而来,散落若干笔误。此类不改动原意的错字已直接改正,在此汇总以便对照:

位置 原文 已改为
§1.5 我们可以通 过 help / 然后通 过 help 我们可以通过 help / 然后通过 help
§2.4 动态传递不同的的的参数值 动态传递不同的参数值
§3.1 下面有 一个子包 下面有一个子包
§4.4 别人可以可以通过 别人可以通过
§5.1 操作系统 功能 操作系统功能
§5.4 几秒种后 几秒钟后
§2.1 代码围栏写作 ```python'(末尾多一个撇号) ```python
全篇 标点附近的冗余半角空格(标点前如 API :、子目录 :;标点后如 目录, 使用命令) 删除多余空格

1 模块化 (module) 程序设计理念

1.1 模块和包概念的进化史

Tip

「量变引起质变」是哲学中的重要理论。为什么会引起质变?本质上,随着数量增加,管理方式会发生本质变化——旧的方式完全不适用,必须改用新的方式。

  1. 程序越来越复杂,语句多了,怎么管理?很自然的,我们会将实现同一个功能的语句封装到函数中,统一管理和调用,于是函数诞生了。

  2. 程序更加复杂,函数和变量多了,怎么管理?同样的思路,「物以类聚」,我们将同一类型对象的「数据和行为」,也就是「变量和函数」,放到一起统一管理和调用,于是「类和对象」诞生了。

  3. 程序继续复杂,函数和类更加多了怎么办?好,我们将实现类似功能的函数和类统统放到一个模块中,于是「模块」诞生了。

  4. 程序还要复杂,模块多了,怎么办?于是,我们将实现类似功能的模块放到一起,于是「包」就诞生了。

Note

可以看到这一发展流程,核心哲学思想就是「量变引起质变」「物以类聚」。同样的思路在企业管理、人的管理中也完全一致,可以举一反三。

image-20220704090231022

模块的本质:在 Python 中,模块的本质就是一个 Python 的独立文件(后缀名.py),里面可以包含全局变量、函数以及类。

注

在 Python 中,每个 Python 文件都可以作为一个模块,模块的名字就是文件的名字。也就是说自定义模块名必须要符合标识符命名规则。

  1. Python 程序由模块组成。一个模块对应一个 Python 源文件,一般后缀名是:.py
  2. 模块由语句组成。运行 Python 程序时,按照模块中语句的顺序依次执行
  3. 语句是 Python 程序的构造单元,用于创建对象、变量赋值、调用函数、控制语句等

1.2 标准库模块 (standard library)

与函数类似,模块也分为标准库模块(内置系统模块)和用户自定义模块。

Python 标准库提供了操作系统功能、网络通信、文本处理、文件处理、数学运算等基本的功能。比如:random(随机数)、math(数学运算)、time(时间处理)、file(文件处理)、os(和操作系统交互)、sys(和解释器交互) 等。

另外,Python 还提供了海量的第三方模块,使用方式和标准库类似。功能覆盖了我们能想象到的所有领域,比如:科学计算、WEB 开发、大数据、人工智能、图形系统等。

原文勘误

原文把 file 与 random、math、time、os、sys 并列,称为标准库中的「文件处理」模块。实测(Python 3.13.14):file 并不是一个模块——

  • importlib.util.find_spec("file") 返回 None,import file 抛 ModuleNotFoundError;
  • file 是 Python 2 的内置类型(file 对象),Python 3 已删除,文件操作统一走内置函数 open() + io 模块。

正确的对应关系是:文件处理用内置函数 open(),底层支撑是标准库 io 模块(import io 实测可用)。后文 §5.1 的同一处表述一并按此理解。

1.3 为什么需要模块化编程

模块(module)对应于 Python 源代码文件(.py 文件)。模块中可以定义变量、函数、类、普通语句。这样,我们可以将一个 Python 程序分解成多个模块,便于后期的重复应用。

模块化编程(Modular Programming)将一个任务分解成多个模块。每个模块就像一个积木一样,便于后期的反复使用、反复搭建。

image-20220704090908839

模块化编程有如下几个重要优势:

  1. 便于将一个任务分解成多个模块,实现团队协同开发,完成大规模程序
  2. 实现代码复用。一个模块实现后,可以被反复调用。
  3. 可维护性增强。

1.4 模块化编程的流程

模块化编程的一般流程:

  1. 设计 API,进行功能描述。
  2. 编码实现 API 中描述的功能。
  3. 在模块中编写测试代码,并消除全局代码。
  4. 使用私有函数实现不被外部客户端调用的模块函数。

1.5 模块的 API 和功能描述要点

API(Application Programming Interface 应用程序编程接口)是模块中提供的函数和类的功能描述和使用方式的描述。

模块化编程中,首先设计的就是模块的 API(即要实现的功能描述),然后开始编码实现 API 中描述的功能。最后,在其他模块中导入本模块进行调用。

我们可以通过 help(模块名)查看模块的 API。一般使用时先导入模块,然后通过 help 函数查看。

案例:导入 math 模块,并通过 help() 查看 math 模块的 API:

import math

help(math)

Pasted-image-20250407120624

也可以在 Python 的 API 文档中查询。首先进入 Python 安装目录下的 docs 子目录:

Snipaste_2022-07-04_09-59-02

双击打开 chm 文档,即可通过索引输入 math查询到对应的 API 内容:

Snipaste_2022-07-04_10-00-09

补充:离线文档的现代做法

原文所说的「安装目录下 docs 子目录 + chm 文档」是 Windows 平台 Python 官方安装包才有的形态,且近年安装包默认不再附带 chm。实测本机(Python 3.13.14):os.path.isdir(os.path.join(sys.prefix, "docs")) 为 False。更通用的做法是直接查阅在线官方文档(见文末参考链接),或对任意对象使用内置 help() / dir() 现场查看。

案例:设计计算薪水模块的 API

"""
本模块用于计门派弟子的薪资
"""
company = "古墓派"

def yearSalary(monthSalary):
    """根据传入的月薪,计算出年薪"""
    pass

def daySalary(monthSalary):
    """根据传入的月薪,计算出每天的薪资"""
    pass

如上模块只有功能描述和规范,需要编码人员按照要求实现编码。

我们可以通过 __doc__ 可以获得模块的文档字符串的内容。

import salary  
  
help(salary)  
help(salary.daySalary)  
  
print(salary.__doc__)  
print(salary.daySalary.__doc__)

Pasted-image-20250407121139

1.6 模块的创建和测试代码

每个模块都有一个名称,通过特殊变量 __name__ 可以获取模块的名称。在正常情况下,模块名字对应源文件名。仅有一个例外,就是当一个模块被作为程序入口时(主程序、交互式提示符下),它的 __name__ 的值为 __main__。我们可以根据这个特点,将模块源代码文件中的测试代码进行独立的处理。例如:

import math

# 输出 'math'
math.__name__

Snipaste_2022-07-04_10-15-36

案例:通过 __name__ == "__main__" 独立处理模块的测试代码

"""
本模块用于计门派弟子的薪资
"""
company = "古墓派"

def yearSalary(monthSalary):
    """根据传入的月薪,计算出年薪"""
    return monthSalary * 12

def daySalary(monthSalary):
    """根据传入的月薪,计算出每天的薪资"""
    # 国家规定每个月的平均工作日是 22.5
    return monthSalary / 22.5

if __name__ == "__main__":  # 测试代码
    print(yearSalary(3000))
    print(daySalary(3000))

1.7 模块文档字符串和 API 设计

我们可以在模块的第一行增加一个文档字符串,用于描述模块的相关功能。然后,通过 __doc__ 可以获得文档字符串的内容。

案例:模块文档字符串示例以及导入后如何读取文档字符串

import salary  
  
# help(salary)  
# help(salary.daySalary)  
  
print(salary.__doc__)  
print(salary.daySalary.__doc__)

Pasted-image-20250407121653

2 模块的导入

2.1 import 语句导入

import 语句的基本语法格式如下:

import 模块名  # 导入一个模块
import 模块 1,模块 2…  # 导入多个模块
import 模块名 as 模块别名  # 导入模块并使用新名字
原文勘误

上面是语法格式示意(占位符不是真实变量名),直接照抄会报错。实测(Python 3.13.14):

  • import 模块名 —— 中文标识符在 Python 3 中合法(PEP 3131),能编译通过;
  • import 模块 1,模块 2 —— 报 SyntaxError: invalid character ',' (U+FF0C)。两个问题:① 逗号用了中文全角 ,,必须用半角 ,;② 模块 1 中的空格会切断标识符,多模块导入不能带空格。

正确写法(同一行导入多个模块,用半角逗号分隔):

import os, sys, math

同理,后文 §2.2 from 模块名 import 成员 1, 成员 2, … 中的 成员 1 也是示意占位,真实代码里成员名不能含空格。

import 加载的模块分为四个通用类别:

  • 使用 Python 编写的代码(.py 文件);
  • 已被编译为共享库或 DLL 的 C 或 C++ 扩展;
  • 一组模块的包
  • 使用 C 编写并链接到 Python 解释器的内置模块;

我们一般通过 import 语句实现模块的导入和使用,import 本质上是使用了内置函数 __import__()。

当我们通过 import 导入一个模块时,Python 解释器会执行它,最终生成一个对象,该对象就代表被加载的模块。

import math  
  
print(f"type(math):{type(math)}")  
print(f"id(math):{id(math)}")  
print(math.pi)  # 通过 math.成员名 来访问模块中的成员

Pasted-image-20250407130948

由上,我们可以看到 math 模块被加载后,实际会生成一个 module 类的对象,该对象被 math 变量引用。我们可以通过 math 变量引用模块中所有的内容。

我们通过 import 导入多个模块,本质上也是生成多个 module 类的对象而已。

有时候,我们也需要给模块起个别名,本质上,这个别名仅仅是新创建一个变量引用加载的模块对象而已。

import math as m  
  
# 等价于  
"""  
import math  
m = math  
"""  
print(m.sqrt(4))  # 开方运算

Pasted-image-20250407131329

2.2 from … import 导入

提问:已经有了 import 导入模块,为什么还需要使用 from 模块名 import 功能名 这样的导入方式?

答:import 会导入模块中的全部功能;但有些情况下我们只想用其中某些方法,无需全部导入,这时就建议采用 from 模块名 import 功能名。

Python 中可以使用 from … import 导入模块中的成员。基本语法格式如下:

from 模块名 import 成员 1, 成员 2, …

如果希望导入一个模块中的所有成员,则可以采用如下方式:

from 模块名 import *
注意

尽量避免 from 模块名 import * 这种写法。 * 它表示将模块中所有不是以 下划线(_) 开头的变量名都导入到当前位置。但是不知道导入了什么变量名,很有可能会覆盖掉之前已经定义好的变量名。而且可读性极其的差。一般生产环境中尽量避免使用,学习时没有关系。

案例:使用 from … import 导入模块指定的成员

from math import pi, sin  
  
print(sin(pi / 2))  # 输出 1.0

Pasted-image-20250407132132

2.3 import 语句和 from … import 语句的区别

import 导入的是模块。 from … import 导入的是模块中的一个函数/一个类。

Tip

如果进行类比的话,import 导入的是「文件」,我们要使用该「文件」下的内容,必须前面加「文件名称」。from … import 导入的是文件下的「内容」,我们直接使用这些「内容」即可,前面再也不需要加「文件名称」了。

我们自定义一个模块 calculator.py:

"""一个实现四则运算的计算器"""

def add(a, b):
    return a + b

def minus(a, b):
    return a - b

class MyNum():
    def print123(self):
        print(123)

我们在另一个模块 test.py 测试:

import calculator

a = calculator.add(30, 40)
# add(100,200) # 不加模块名无法识别
print(a)

Pasted-image-20250407133013

from calculator import *

a = add(100, 200)  # 无需模块名,可以直接引用里面的函数/类
print(a)
b = MyNum()
b.print123()

Pasted-image-20250407133110

小结:两种导入方式对照
对比项 import calculator from calculator import add
导入的对象 模块对象本身 模块中的成员(函数/类/变量)
调用方式 必须加模块名前缀:calculator.add(30, 40) 直接用成员名:add(30, 40)
命名冲突风险 低(成员都在模块命名空间下) 高(成员直接进入当前命名空间)
回到本源 生成一个 module 对象,被 calculator 变量引用 把目标对象绑定到当前模块的一个新名字上

2.4 __import__() 动态导入

import 语句本质上就是调用内置函数 __import__(),我们可以通过它实现动态导入。给 __import__() 动态传递不同的参数值,就能导入不同的模块。

案例:使用 __import__() 动态导入指定的模块

s = "math"
# 导入后生成的模块对象的引用给变量 m
m = __import__(s)
print(m.pi)

Pasted-image-20250407133150

注意

一般不建议自行使用 __import__() 导入,其行为在 Python 2 和 Python 3 中有差异,可能导致意外错误。如需动态导入,可以使用 importlib 模块。

import importlib

a = importlib.import_module("math")
print(a.pi)

Pasted-image-20250407134326

实测:__import__() 与 importlib.import_module() 的关键差异

原文说「行为在 Python 2 和 Python 3 中有差异」,实测在同一个 Python 3 内两者也有实质差异(Python 3.13.14,目录结构 pkgsub/sub.py):

写法 返回的对象 __name__
__import__("pkgsub.sub") 顶层包 pkgsub
__import__("pkgsub.sub", fromlist=["sub"]) 目标子模块 pkgsub.sub
importlib.import_module("pkgsub.sub") 目标子模块 pkgsub.sub

即 __import__("a.b") 默认只返回包 a,想拿到 a.b 得额外传 fromlist。这正是推荐用 importlib.import_module() 做动态导入的原因——它直接返回你要的那个模块。

2.5 模块的加载问题

当导入一个模块时,模块中的代码都会被执行。不过,如果再次导入这个模块,则不会再次执行。

Python 的设计者为什么这么设计?因为导入模块多数时候只是为了得到其中的变量、函数、对象等定义,并不需要反复定义和执行。于是「只导入一次」(import-only-once)就成了一种优化。

一个模块无论导入多少次,这个模块在整个解释器进程内有且仅有一个实例对象。

test02.py 的源代码:

print("test 模块被加载了 ...")

test03.py 的源代码:

# 会执行 test02 模块中的语句
import test02
# 不会再执行 test02 模块中的语句
import test02

Pasted-image-20250407135057

实测:连续 3 次 import 只执行一次(真实运行结果)

把 test03.py 改成连续导入 3 次:

import test02
import test02
import test02
print('t03 done')

运行输出(Python 3.13.14):

t02 模块被加载了 ...
t03 done

"t02 模块被加载了" 只出现 1 次——第 2、3 次导入命中 sys.modules 缓存直接返回,不再执行模块体。实测 sys.modules["math"] is math 为 True,缓存条目数约 105,这就是「有且仅有一个实例对象」的载体。

重新加载

有时确实需要重新加载一个模块,这时可以用 importlib.reload() 方法:

import test02
import test02

print("####")
import importlib

importlib.reload(test02)

Pasted-image-20250407135229

实测:importlib.reload() 会再执行一次模块体(真实运行结果)

运行上面代码,输出(Python 3.13.14):

t02 模块被加载了 ...
####
t02 模块被加载了 ...

import test02 两次只打印 1 次,而 importlib.reload(test02) 又打印了 1 次——证实 reload 会重新执行模块顶层代码。

3 包 package 的使用

3.1 包 (package) 的概念和结构

Pasted-image-20250407135358

当一个项目中有很多个模块时,需要再进行组织。我们将功能类似的模块放到一起,形成了「包」。本质上,「包」就是一个必须有 __init__.py 的文件夹。典型结构如下:

Snipaste_2022-07-04_20-51-32

包下面可以包含「模块 (module)」,也可以再包含「子包 (subpackage)」。就像文件夹下面可以有文件,也可以有子文件夹一样。

Snipaste_2022-07-04_20-52-03

上图中,a 是上层的包,下面有一个子包:aa。可以看到每个包里面都有 __init__.py 文件。

补充:__init__.py 并非「必须有」(PEP 420 命名空间包)

原文说「本质上,‘包’就是一个必须有 __init__.py 的文件夹」。这句话在 Python 3.3 之前成立;实测(Python 3.13.14):没有 __init__.py 的目录也能作为包被导入,这类包叫命名空间包(namespace package,PEP 420)。

实测目录结构(无 __init__.py):
nspkg/
  mod_x.py     ← 内容:VAL = 'nspkg.mod_x'

>>> import nspkg.mod_x
>>> nspkg.mod_x.VAL
'nspkg.mod_x'          # 导入成功!

两者区别:

类型 是否有 __init__.py 特点
常规包(regular package) 有 导入时执行 __init__.py,可写初始化逻辑、__all__
命名空间包(namespace package) 无 仅共享一个目录命名空间,不能写初始化代码

教程里仍推荐加 __init__.py:它是常规包的标识,能承载初始化与批量导入(见 §3.3),也不会与老版本工具链冲突。原文的表述当作「推荐做法」理解即可。

3.2 PyCharm 中创建包

在 PyCharm 开发环境中创建包非常简单。在要创建包的位置单击右键:New-->Python package 即可,PyCharm 会自动生成带有 __init__.py 文件的包。

Snipaste_2022-07-04_20-53-26

3.3 导入包操作和本质

Snipaste_2022-07-04_20-52-03_1

上一节中的包结构,我们需要导入 module_AA.py。方式如下:

  1. import a.aa.module_AA
    在使用时,必须加完整名称来引用。比如:a.aa.module_AA.fun_AA()
  2. from a.aa import module_AA
    在使用时,直接可以使用模块名。比如:module_AA.fun_AA()
  3. from a.aa.module_AA import fun_AA 直接导入函数
    在使用时,直接可以使用函数名。比如:fun_AA()
Tip
  1. from package import item 这种语法中,item 可以是包、模块;也可以是函数、类、变量。
  2. import item1.item2 这种语法中,item 必须是包或模块,不能是其他。

导入包的本质其实是导入了包的 __init__.py 文件。也就是说,import pack1 意味着执行了包 pack1 下面的 __init__.py 文件。这样,可以在 __init__.py 中批量导入我们需要的模块,而不再需要一个个导入。

__init__.py 的三个核心作用:

  1. 作为包的标识,不能删除。
  2. 用来实现模糊导入
  3. 导入包实质是执行 __init__.py 文件,可以在 __init__.py 文件中做这个包的初始化、以及需要统一执行代码、批量导入。

示例:测试包的 __init__.py 文件本质用法

a 包下的 __init__.py 文件内容:

import turtle
import math

print("导入 a 包")

b 包下的 module_B1.py 文件中导入 a 包,代码如下:

import a

print(a.math.pi)

Snipaste_2022-07-04_22-02-16

Tip

由上面的测试可以看出,Python 的设计者非常巧妙地通过 __init__.py 文件把「包」转成了「模块」来操作。

3.4 用 * 导入包

Snipaste_2022-07-04_20-52-03_2

import * 这样的语句理论上是希望文件系统找出包中所有的子模块,然后导入它们。这可能会花长时间等。Python 解决方案是提供一个明确的包索引。

这个索引由 __init__.py 定义 __all__ 变量,该变量为一列表,如上例 a 包下的 __init__.py 中,可定义 __all__ = ["module_A", "module_A2"]

这意味着,from a import * 会从对应的包中导入以上两个子模块。

注意

尽管提供 import * 的方法,仍不建议在生产代码中使用这种写法。

实测:__all__ 确实能精确控制 from pkg import * 的范围(真实运行结果)

构造包 regpkg,__init__.py 内容为 __all__ = ['module_A'],目录下另有 module_A.py、module_A2.py、not_all.py:

from regpkg import *
print(sorted(k for k in dir() if not k.startswith('__')))

运行输出(Python 3.13.14):

regpkg/__init__.py 被执行
['module_A']

只有 __all__ 里列出的 module_A 被导入,未列出的 module_A2、not_all 均未被引入——__all__ 是一份白名单。

3.5 包内引用

如果是子包内的引用,可以按相对位置引入子模块。

以 aa 包下的 module_AA 导入 a 包内容为例:

from .. import module_A # .. 表示上级目录 .表示同级目录
from . import module_A2 # . 表示同级目录
实测:相对导入只能在「包内」生效

实测(Python 3.13.14,包结构 a/aa/module_AA.py):

  • 以包内模块方式导入(import a.aa.module_AA)→ 成功,from .. import module_A 与 from . import module_A2 均正常解析;
  • 以脚本方式直接运行 python module_AA.py → 报错 ImportError: attempted relative import with no known parent package。

原因:相对导入需要一个「已知的父包」来定位 . 和 ..。__name__ 为 __main__ 时没有父包,所以含相对导入的模块只能用 -m 方式或作为包的一部分被导入,不能双击直接跑。

3.6 sys.path 和模块搜索路径

导入某个模块文件时,Python 解释器去哪里找它?只有找到这个文件,才能读取、装载并运行该模块。它一般按如下路径依次查找(按顺序查找,找到即停,不再往下):

  1. 内置模块
  2. 当前目录
  3. 程序的主目录
  4. pythonpath 目录(如果已经设置了 pythonpath 环境变量)
  5. 标准链接库目录
  6. 第三方库目录(site-packages 目录)
  7. .pth 文件的内容(如果存在的话)
  8. sys.path.append() 临时添加的目录

任何一个 Python 程序启动时,都会把这些搜索路径(内置模块除外)收集起来,放进 sys 模块的 path 属性(sys.path)。

实测:sys.path 的真实内容(Python 3.13.14,真实运行结果)
0: 'D:\\obData\\note\\.workbuddy\\tmp\\py11\\probe'      ← 脚本所在目录(搜索顺序第 2 条,见下)
1: '...\\cli\\vendor\\shim'                              ← PYTHONPATH 环境变量注入
2: '...\\python313.zip'                                  ← 标准库压缩包
3: '...\\DLLs'
4: '...\\Lib'                                            ← 标准链接库目录
5: '...\\versions\\3.13.12'
6: '...\\envs\\default'
7: '...\\envs\\default\\Lib\\site-packages'              ← 第三方库目录

要点:sys.path[0] 就是当前脚本所在目录(交互式下是 ''),这正是「当前目录/主目录」优先于 site-packages 的原因;PYTHONPATH 环境变量会被插到标准库路径之前。内置模块(如 math、sys)不在 sys.path 上,它们编译进了解释器——实测 math in sys.builtin_module_names 为 True。

⚠️ 注意:以脚本方式运行时 sys.path[0] 是脚本目录,而不是 shell 的当前工作目录(cwd)。初学者常混淆这两者。

1、使用 sys.path 查看和临时修改搜索路径

我们在项目的 b 目录下建立测试模块:

import sys

sys.path.append("d:/")
print(sys.path)

执行结果:

Snipaste_2022-07-05_13-31-34

2、pythonpath 环境变量的设置

windows 系统中通过如下操作添加和设置 pythonpath 环境变量。

Snipaste_2022-07-05_13-35-24

Snipaste_2022-07-05_13-35-43

Snipaste_2022-07-05_13-35-54

3、.pth 文件的写法

我们可以在 site-packages 目录下添加 .pth 文件。并在文件中增加内容:

#一行一个目录
g:\a
g:\b
g:\c
原文勘误:上面这段不是 Python 代码

原文把 .pth 文件内容放进了 ```python 代码块,但它是纯文本配置(一行一个路径),不是 Python 语句。已改为 text 块(见上)。原文如此标注会误导初学者去「运行」它。

注意
  1. 需确保 g:\a, g:\b, g:\c 对应的目录真实存在。
  2. 在 windows 系统中建立 .pth 文件,由于没有文件名不能直接建立。需要输入:.pth. 才能正常建立 .pth 文件。
补充:.pth 文件只在 site-packages 里生效

.pth 文件由 site 模块在启动时扫描指定目录(site-packages)下的 *.pth,把每行路径追加进 sys.path。实测本机 site-packages 下已有 pywin32.pth 在起作用。因此 .pth 文件必须放在 site-packages(或用户级 site-packages)里,放在项目目录下不会被读取。

4 模块发布和安装

4.1 模块的本地发布

当我们完成了某个模块开发后,可以将它对外发布,其他开发者也可以以「第三方扩展库」的方式使用我们的模块。我们按照如下步骤即可实现模块的发布:

  1. 为模块文件创建如下结构的文件夹(一般,文件夹的名字和模块的名字一样):

    Snipaste_2022-07-05_15-01-44

  2. 在文件夹中创建一个名为 setup.py 的文件,内容如下:

    from distutils.core import setup
    
    setup(
        name='baizhan Math 2',  # 对外我们模块的名字
        version='1.0',  # 版本号
        description='这是第一个对外发布的模块,测试哦',  # 描述
        author='gaoqi',  # 作者
        author_email='gaoqi110@163.com', 
        py_modules=['baizhanMath2.demo1', 
     		       'baizhan Math 2.demo2']  # 要发布的模块
    )
    
  3. 构建一个发布文件。通过终端,cd 到模块文件夹 c 下面,再键入命令:

    python setup.py sdist
    

    执行完毕后,目录结构变为:

    Snipaste_2022-07-05_15-12-38

原文勘误:distutils 已在 Python 3.12 被移除

原文 setup.py 用的是 from distutils.core import setup。实测(Python 3.13.14):importlib.util.find_spec("distutils") 返回 None,from distutils.core import setup 抛 ModuleNotFoundError。

依据 PEP 632(Python 3.12 What's New 明确列出「Remove the distutils package」):distutils 自 3.10/3.11 起弃用,3.12 起从标准库移除。官方建议改用第三方 setuptools(它内置了 distutils 的完整副本)。

现代等价写法:

from setuptools import setup

更推荐的新式做法是用 pyproject.toml 声明构建配置 + python -m build 出包(见 §4.3 勘误)。

4.2 本地安装模块

将发布安装到你的本地计算机上。仍在 cmd 命令行模式下操作,进 setup.py 所在目录,键入命令:

python setup.py install

安装成功后,进入 Python 目录下的 /Lib/site-packages 目录(第三方模块都安装在这里,Python 解释器执行时也会搜索这个路径):

Snipaste_2022-07-05_15-15-14

安装成功后,直接使用 import 导入即可。

import baizhanMath2.demo1
原文勘误:python setup.py install 已被废弃

原文用 python setup.py install 安装。依据官方 Packaging 文档(Setup.py 弃用说明):python setup.py install 自 setuptools 58.3.0 起被废弃,且直接调用 setup.py 的方式整体不推荐。

现代做法是先用 pip wheel / python -m build 产出 wheel,再 pip install dist/xxx.whl;或直接 pip install .(在含 pyproject.toml/setup.py 的项目目录下执行)。实测本机环境已无 setuptools(find_spec("setuptools") 为 False),需先 pip install setuptools 才能用 setup() 写法。

4.3 上传模块到 PyPI

将自己开发好的模块上传到 PyPI 网站上,将成为公开的资源,可以让全球用户自由使用。按照如下步骤做,很容易就实现上传模块操作。

1、注册 PyPI 网站

注册 PyPI 网站:http://pypi.python.org

Snipaste_2022-07-05_15-20-42

注意:会发送一封邮件到你的邮箱。请点击验证后继续下面的步骤。

2、创建用户信息文件 .pypirc

方式 1:使用命令 (适用 Linux)

输入并执行后 python setup.py register,然后输入用户名和密码,即可。

方式 2:使用文件(适用 windows,Linux)

在用户的家目录里创建一个文件名为 .pypirc,内容为:

[distutils]
index-servers=pypi
[pypi]
repository = https://upload.pypi.org/legacy/
username = 账户名
password = 你自己的密码
原文勘误:setup.py register 已停止服务

原文「方式 1」的 python setup.py register 已不可用。依据官方 Packaging 文档与 PyPI 迁移说明:register 命令已废弃并从 setuptools 移除,直接调用会收到服务器响应 410: This API is no longer supported, instead simply upload the file.。首次上传无需再单独注册包名,直接上传即可占用名字。

此外 .pypirc 也不应再明文存密码:现代上传工具 twine 支持从命令行/环境变量(TWINE_USERNAME / TWINE_PASSWORD)或 keyring 读取凭据,官方推荐用 API Token 而非账户密码。

Tip

Linux 的家目录: ~/.pypirc
Windows 的家目录是: c:/user/用户名

在 windows 下直接创建不包含文件名的文件会失败,因此创建时文件名为 .pypirc.,前后都有两个点即可。

3、上传并远程发布

进入 setup.py 文件所在目录,使用命令 python setup.py sdist upload,即可以将模块代码上传并发布:

Snipaste_2022-07-05_15-32-55

原文勘误:setup.py sdist upload 已被移除

原文的核心上传命令 python setup.py sdist upload 现在无法使用——upload 子命令已随 distutils 一起移除(PEP 632),PyPI 也不再接受账户密码上传。

官方现行流程(2026 年):

# 1. 安装构建与上传工具
python -m pip install --upgrade build twine
# 2. 构建分发文件(产出 dist/*.tar.gz 与 dist/*.whl)
python -m build
# 3. 用 twine 上传,凭据用 API Token(或 TWINE_USERNAME/__token__)
python -m twine upload dist/*

依据:Packaging 官方文档「Is setup.py deprecated?」指明 setup.py check/register/upload 的替代工具是 twine;PyPI 已完全停用账户密码上传,须用作用域 API Token(或在 CI 中用 Trusted Publishing)。

4、管理你的模块

我们登录 pypi 官网,可以看到:

如果你的模块已经上传成功,那么当你登录 PyPI 网站后应该能在右侧导航栏看到管理入口。

Snipaste_2022-07-05_15-33-56

点击包名进去后你可以对你的模块进行管理,当然你也可以从这里删除这个模块。

4.4 让别人使用你的模块

模块发布完成后,其他人只需要使用 pip install 就可以安装你的模块文件。比如:

pip install package-name

Snipaste_2022-07-05_15-38-13

如果你更新了模块,别人可以通过 --upgrade 参数来更新:

Snipaste_2022-07-05_15-42-12

原文勘误:更新模块的参数是 --upgrade(不是 --update)

原文正文写作「别人可以可以通过 --update 参数来更新」。实测 pip install --help(pip 26.1.2):不存在 --update 参数,正确的是 -U, --upgrade——「Upgrade all specified packages to the newest available version」。

  • 原文 · 是一个丢失的占位符(应为 pip install),已补全为 pip install;
  • 更新命令应为:
pip install --upgrade package-name

另注:pip install ... 是命令行命令,不是 Python 代码。原文放在 ```python 块中,已改为 bash。

5 库 (Library)

「库」是借用其他编程语言的概念,在 Python 中没有特别具体的定义。

模块和包侧重于代码组织,有明确的定义;库则强调功能性,而不是代码组织。

我们通常把某个功能的模块集合称为库。

5.1 标准库 (Standard Library)

Python 拥有一个强大的标准库。Python 语言的核心只包含数字、字符串、列表、字典、文件等常见类型和函数,而 Python 标准库则提供了系统管理、网络通信、文本处理、数据库接口、图形系统、XML 处理等额外的功能。

Python 标准库的主要功能有:

  1. 文本处理,包含文本格式化、正则表达式匹配、文本差异计算与合并、Unicode 支持,二进制数据处理等功能
  2. 文件处理,包含文件操作、创建临时文件、文件压缩与归档、操作配置文件等功能
  3. 操作系统功能,包含线程与进程支持、IO 复用、日期与时间处理、调用系统函数、日志(logging)等功能
  4. 网络通信,包含网络套接字,SSL 加密通信、异步网络通信等功能
  5. 网络协议,支持 HTTP,FTP,SMTP,POP,IMAP,NNTP,XMLRPC 等多种网络协议,并提供了编写网络服务器的框架
  6. W3C 格式支持,包含 HTML,SGML,XML 的处理
  7. 其它功能,包括国际化支持、数学运算、HASH、Tkinter 等
Tip

目前学过的有:random、math、time、file、os、sys 等模块。

  1. random 模块实现随机数处理
  2. math 模块实现数学相关的运算
  3. time 模块实现时间的处理
  4. file 模块实现对文件的操作
  5. OS 模块实现和操作系统的交互
  6. sys 模块实现和解释器的交互
勘误复用

上面第 4 条的 file 模块不存在,与 §1.2 是同一处笔误,详见 §1.2 原文勘误。文件操作请用内置函数 open() 与标准库 io 模块。

5.2 第三方扩展库的介绍

强大的标准库奠定了 Python 发展的基石,丰富且不断扩展的第三方库则是 Python 壮大的保证。可以进入 PyPI 官网:https://pypi.org

Snipaste_2022-07-05_15-56-29

我们可以看到发布的第三方库达到了十多万种,众多的开发者为 Python 贡献了自己的力量。

常用第三方库大汇总

原文勘误:这里原本是一个空链接

原文此处的 [常用第三方库大汇总]() 是空链接(() 内无 URL),点击无任何效果。已改为纯文本。如需选库,建议直接检索 PyPI(https://pypi.org)或参考 Awesome Python 系列清单。

另据 PyPI 官方统计口径,PyPI 上的项目数早已超过 50 万(原文「十多万种」为早期数据,此处保留原意并备注现状)。

5.3 PyPI 网站和 PIP 模块管理工具

PyPI(Python Package Index)是 Python 官方的第三方库仓库,所有人都可以下载第三方库,或上传自己开发的库到 PyPI。PyPI 推荐使用 pip 包管理器来下载第三方库。

pip 是一个现代、通用的 Python 包管理工具,提供对 Python 包的查找、下载、安装、卸载功能。pip 可运行在 Windows、Mac OS、Unix/Linux 等操作系统上,但需要至少 2.6+ 和 3.2+ 的 CPython 或 PyPy 支持。Python 2.7.9 和 3.4 以后的版本已内置 pip 程序,无需单独安装。

5.4 安装第三方扩展库的 2 种方式

第三方库有数十万种之多,以 pymysql 库为例讲解第三方扩展库的安装。

第一种方式:命令行下远程安装

  1. pip 更换数据源 (由于访问国外网站慢,建议更换):
Tip

家目录中,创建 pip 目录,然后增加文件:pip.ini,内容拷贝下面的即可(不要加其他字符):

[global]
index-url=https://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com

Linux 的家目录:~
增加目录和文件:~/.pip/pip.conf
Windows 的家目录是:c:/user/用户名
增加目录和文件:c:/user/用户名/pip/pip.ini

  1. 其他数据源:
  1. 以安装第三方 pymysql 库为例,在命令行提示符下输入:pip install pymysql 即可。
更快的临时换源方式

不必改配置文件,单次安装也能临时指定数据源:

pip install pymysql -i https://mirrors.aliyun.com/pypi/simple/

测速后可在配置文件里永久生效;多条镜像源同样适用于 -i 参数。

第二种方式:PyCharm 中直接安装到项目中

在 PyCharm 中,依次点击:file-->setting-->Project 本项目名-->Project Interpreter

Pasted-image-20250407150230

点击 +,然后输入要安装的第三方库 pymysql,再点击按钮 Install Package,等待安装即可,几秒钟后,即提示安装成功:

Pasted-image-20250407150319

这样就可以在项目中直接使用第三方库 pymysql 了。

6 术语表(重要英文单词)

6.1 模块与包基础

术语 全拼/说明 释义
module —— 模块;一个 .py 文件就是一个模块,是代码组织的最小单位
package —— 包;一个含 __init__.py 的目录(推荐做法),用于组织多个模块
subpackage —— 子包;包内部的包,层层嵌套
standard library —— 标准库;随 Python 一起安装的官方模块集合
library —— 库;强调功能性的一组模块集合
__init__.py —— 包的初始化文件,导入包时被执行,承载初始化与批量导入
regular package —— 常规包;含 __init__.py 的包
namespace package —— 命名空间包(PEP 420);无 __init__.py,仅共享目录命名空间
__all__ —— 包/模块中定义的公开名单列表,控制 from x import * 导入范围
modular programming —— 模块化编程;把任务拆成多个模块来组织

6.2 导入机制

术语 全拼/说明 释义
import —— 导入语句;导入模块对象,使用需加模块名前缀
from … import —— 从模块中导入指定成员,使用无需前缀
import … as … —— 导入并起别名;仅创建新的变量引用
__import__() —— 内置函数;import 语句的底层实现,可动态导入
importlib import library 导入机制的标准库模块,提供 import_module / reload
importlib.import_module() —— 按字符串动态导入,直接返回目标模块(含子模块)
importlib.reload() —— 重新加载已导入的模块,会再次执行模块顶层代码
import-only-once —— 只导入一次;同一进程内重复导入不重复执行模块体
sys.modules —— 已加载模块的缓存字典,import-only-once 的载体
sys.path —— 模块搜索路径列表,决定解释器去哪找模块
sys.builtin_module_names —— 编译进解释器的内置模块名元组
PYTHONPATH —— 环境变量;其路径会被插入 sys.path 前部
site-packages —— 第三方库安装目录,也在 sys.path 上
.pth file path file site-packages 下的路径配置文件,每行一个目录追加进 sys.path
relative import —— 相对导入;用 .(同级)/ ..(上级)定位包内模块
API Application Programming Interface 应用程序编程接口;模块对外提供的功能描述与调用方式
PyPI Python Package Index Python 官方第三方库仓库(https://pypi.org)

6.3 分发与安装

术语 全拼/说明 释义
setup.py —— 传统打包脚本;调用 setup() 声明包元数据
setup() —— 打包函数;声明 name/version/author/py_modules 等
distutils distribution utilities 旧标准库打包工具,Python 3.12 起已移除(PEP 632)
setuptools —— 现代打包工具,内置 distutils 副本,setup() 现由它提供
pyproject.toml —— 现代项目配置文件;声明构建后端与项目元数据(PEP 517/518/621)
sdist source distribution 源码分发包(.tar.gz)
wheel —— 二进制分发格式(.whl),pip 优先安装
python -m build —— 现代构建命令,按 pyproject.toml 产出 sdist 与 wheel
twine —— 官方推荐的上传工具,替代已移除的 setup.py upload
.pypirc —— 上传凭据配置文件;现代用法建议改用 API Token
API token —— PyPI 作用域令牌;替代账户密码用于上传
pip pip installs packages Python 包管理工具;查找/下载/安装/卸载
-i / --index-url —— pip 指定数据源(镜像源)参数
-U / --upgrade —— pip 升级已安装包到最新版(不是 --update)

7 系列导航

Python 基础系列
  • 1.Python 入门
  • 2.编程基础概念
  • 3.Python 中的基础数据类型
  • 4.字符串
  • 5.内建数据结构
  • 6.控制语句
  • 7.函数用法
  • 8.面向对象编程
  • 9.异常和错误
  • 10.文件操作(IO 技术)
  • 11.模块 (module)(本篇)
  • 12.正则表达式 ★★★
  • 13.并发编程
  • 14.网络编程
  • 15.Python操作MySQL

8 参考链接

分类:Python

posted @ 2026-04-11 02:01  挖掘鱼  阅读(49)  评论(0)    收藏  举报