模块化与包

1 模块概述

Python 中一个以.py结尾的源文件即为一个模块(Module),内部可包含变量、函数、类、常量等代码实体。将特定功能的代码封装为模块,核心价值在于:

  • 复用性:编写一次可在多个项目中导入使用,避免重复编码
  • 可维护性:单个模块聚焦单一功能,便于调试和迭代
  • 命名隔离:不同模块中可存在同名函数 / 变量,互不干扰
  • 工程化:大型项目通过模块拆分实现分工协作,降低复杂度

模块名遵循标识符命名规则,严禁与 Python 标准库(如os、sys)或第三方库重名,否则会引发命名冲突。

2 模块创建

模块名区分大小写,且不能与 Python 自带的标准模块重名。

命名总结

1
2
3
4
5
6
7
8
标准 Python 代码中仍推荐使用蛇形命名法
-模块名:module_name.py
-函数名:function_name
-变量名:variable_name
-常量名:CONSTANT_NAME
只有类名使用大驼峰
-类名:ClassName
但是,与其他语言或系统配合时,经常会看到数据命名以小驼峰式命名。所以有些人(尤其是掌握多种语言的人)在写的过程中,可能会习惯性的使用小驼峰式,是可以的,环境接受即可。

2.1 基础模块创建

创建一个模块my_add.py

1
2
3
4
num =100
def add(a, b):
    """求两个数的和"""
    return a + b

2.2 模块设计最佳实践

  1. 文档字符串:模块开头添加功能说明,函数 / 类添加用途、参数、返回值注释

  2. 类型注解:为函数参数和返回值添加类型提示,提升可读性和 IDE 支持

  3. 私有成员:内部辅助函数 / 变量以单下划线_开头,对外隐藏实现细节

  4. 自测代码:通过if __name__ == "__main__"编写模块自测逻辑,不影响导入使用

    1
    2
    if __name__ == "__main__":
    print("模块自测")
  5. 常量命名:模块级常量使用全大写 + 下划线命名(如MAX_RETRY

练习,有如下需求:

定义数学工具模块math_tools

  • 基础算术运算函数
    • add(a: int | float, b: int | float) -> int | float
      功能:计算两个数的和
      参数:两个数值类型参数 a 和 b
      返回值:两数之和
    • multiply(a: int | float, b: int | float) -> int | float
      功能:计算两个数的乘积
      参数:两个数值类型参数 a 和 b
      返回值:两数之积
  • 数列计算函数
    • fibonacci(n: int) -> int
      功能:计算斐波那契数列第n项的值
      参数:非负整数 n(项数)
      返回值:斐波那契数列第n项的数值
      异常处理:当输入负数时抛出 ValueError
  • 数学常量
    • PI:圆周率常量,值为3.1415926535
    • EULER:自然常数e,值为2.7182818284
  • 辅助函数,用于将计算结果格式化,小数保留2位
    • _format_number(n: int | float) -> str
      功能:格式化数字显示(内部私有函数)
      参数:数值类型参数 n
      返回值:格式化后的字符串
  • 给与自测方法

实现:一个功能完整的数学工具模块示例(math_tools.py):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
"""
数学工具模块
包含常用的算术运算、数值处理功能
"""

# 常量
PI = 3.1415926
E = 2.71828

# 普通函数
def add(a: int | float, b: int | float):
"""求两个数的和"""
return a + b

def multiply(a: int | float, b: int | float):
"""求两个数的积"""
# return a * b
return _format_number(a * b)

#数值:1 1 2 3 5 8 ...
#项数:1 2 3 4 5 6 ...
def fibonacci(n: int) :
"""
斐波那契数列
:param n: 项数
:return: 项值
"""
if n < 0:
raise ValueError("项数不能为负数")
elif n == 1 or n ==2:
return 1

return fibonacci(n-1) + fibonacci(n-2)


def _format_number(n: int | float):
"""
# 辅助函数,用于将计算结果格式化,小数保留2位
:param n: 要格式化的整数或者小数
:return: 格式化后的字符串
"""
return f"{n:.2f}" if isinstance(n, float) else str(n)

# 代码自测
if __name__ == "__main__":
print(add(1, 2))
print(multiply(1, 2))
print(fibonacci(10))
print(f"2 + {PI} = {add(2, PI)}")
print(f"2 + {PI} = {_format_number(add(2, PI))}")

3 导入模块

导入操作需要在另外一个模块中操作:

同级目录创建main.py,本节案例均在main.py中导入上个案例练习中的math_tools,并使用

3.1 全部导入 import

导入模块所有成员,通过模块名.成员名访问,适合需要使用模块多个功能的场景,同一模块多次导入仅生效一次。

语法:import 模块名 [as 别名]

3.1.1 基础用法

1
2
3
4
5
6
7
# 导入模块
import math_tools

# 使用模块成员
print(math_tools.add(2, 3)) # 输出:5
print(math_tools.PI) # 输出:3.1415926535
print(math_tools.is_prime(11)) # 输出:True

3.1.2 别名导入(解决命名冲突)

当模块名过长或存在命名冲突时,可指定别名:

1
2
3
4
5
6
# 导入时指定别名
import math_tools as mt

# 通过别名访问
print(mt.multiply(3, 4)) # 输出:12
print(mt.EULER) # 输出:2.7182818284
1
2
3
4
5
6
7
8
注意dict只对导入的模块对象起到作用,无法在自身模块中使用

当前模块使用以下方式获取dict内容
print(globals())
print(sys.modules[__name__].__dict__)

import __main__
print(__main__.__dict__)

3.2 局部导入(from...import

指定导入模块的部分成员,直接通过成员名访问,减少代码冗余,重名成员后导入会覆盖前导入。

3.2.1 导入特定成员

语法:from 模块名 import 成员名1[as 别名], 成员名2[as 别名],…

1
2
3
4
5
6
# 导入模块中的指定函数和常量
from math_tools import add, PI

# 直接使用成员名
print(add(5, 7)) # 输出:12
print(PI * 2) # 输出:6.283185307

3.2.2 别名避免冲突

1
2
3
4
5
# 导入时为成员指定别名(解决同名冲突)
from math_tools import multiply as mul, fibonacci as fibo

print(mul(2, 8)) # 输出:16
print(fibo(13)) # 输出:True

3.3 全部局部导入(from...import *

导入模块中所有非单下划线开头的成员,适合临时测试或快速使用模块全部功能。

  1. 语法:from 模块名 import *
  2. 案例:
1
2
3
4
from my_add import *
print(add(1, 2)) # 输出3
print(num) # 输出100
# print(_str1) # 无法访问以下划线开头的成员

3.4 导入方式区别与选择

3.4.1 导入区别

  • 命名空间影响
    • import 模块:将模块对象本身放入当前命名空间,使用需加前缀(如:模块.成员)。
    • from 模块 import 名称:把指定名称直接放入当前命名空间,使用时无需模块前缀。
    • **from 模块 import *:把模块中“公开”的名称批量放入当前命名空间;若模块定义了 all,则只导入列表中的名称;若未定义,通常导入所有不以下划线 _**开头的名称。此方式不会把“模块对象本身”带入命名空间(即不能写“模块.成员”)。
  • 可读性与可维护性
    • 直接 import 能清晰看出成员来源,便于追踪与重构;
    • import * 可读性差,易造成名称来源不明确与维护困难。
  • 命名冲突与遮蔽
    • import 通过前缀隔离,冲突少;
    • from … import(尤其是 *)容易与当前作用域名称冲突或被遮蔽。
  • 性能与缓存
    • 两种方式都会利用 sys.modules 缓存,已导入模块不会重复执行;性能差异通常不明显,更多体现在可读性与维护性上

3.4.2 选择建议

如何选择更合适的方式

  • 优先选择:import 模块import 模块 as 别名。理由是来源清晰、命名空间干净、冲突少,长期维护成本更低。
  • 可接受的选择:from 模块 import 名称1, 名称2。在明确只用到少量名称、且希望减少前缀冗余时使用;必要时配合 as 避免冲突。
  • 尽量避免:**from 模块 import ***。仅在交互式会话或文档明确推荐的场景下使用;在库/生产代码中会降低可读性与可维护性。

典型用法与等价示例

  • 直接导入

    1
    2
    3
    import math
    # 开平方
    print(math.sqrt(4)) # 2.0
  • 选择性导入

    1
    2
    3
    from math import sqrt, pi

    print(sqrt(4), pi) # 2.0 3.141592653589793
  • 别名导入(避免冲突、缩短名称)

    1
    2
    3
    4
    5
    import numpy as np  
    from datetime import datetime as dt

    print(dt.now())
    print(np.pi)
  • 星号导入(谨慎使用)

    1
    2
    3
    4
    # 假设有模块m,其中有foo与Bar成员
    from m import *
    foo()
    Bar()

3.5 模块搜索顺序

Python 导入模块时,按以下顺序查找:

  1. 当前执行脚本所在目录(优先级最高)
  2. sys.path列表中的目录(环境变量PYTHONPATH配置)
  3. Python 标准库目录
  4. 第三方库安装目录(如site-packages
1
2
3
4
5
6
7
8
9
10
11
import sys

# 查看当前模块搜索路径
print("默认搜索路径:")
for path in sys.path[:5]: # 打印前5个路径
print(f" {path}")

# 临时添加自定义路径(程序重启后失效)
sys.path.append("D:/xxx") # 添加自定义模块目录
print("\n添加后的搜索路径:")
print(f" {sys.path[-1]}")

3.6 __all__控制导出

__all__是模块级列表,控制模块组件导出,用于指定from...import *时可导入的成员,仅对该导入方式有效。

修改 my_add.py,添加__all__

1
2
3
4
5
6
7
8
__all__ = ["num", "add"]  # 仅允许导入num和add

num = 100
num1 = 200

def add(a, b):
"""求两个数的和"""
return a + b

导入测试:

1
2
3
4
5
6
7
8
from my_add import *
print(add(1, 2)) # 输出3
print(num) # 输出100
# print(num1) # 报错:NameError: name 'num1' is not defined(未在__all__中)

# 全部导入不受__all__影响
import my_add
print(my_add.num1) # 输出200

3.7 __name__属性

``name`是 Python 模块的内置属性,是模块身份标识,用于标识模块运行状态:

  • 文件直接运行时,__name__值为 “main”;
  • 文件作为模块导入时,__name__值为模块名(不含.py 后缀)。

实际应用场景

定义被导入使用的模块

1
2
3
4
5
6
7
8
9
10
# 模块A:module_a.py
def func_a():
print("执行模块A的函数")

# 自测代码(仅直接运行时执行)
if __name__ == "__main__":
print("模块A直接运行,执行自测")
func_a()

print("其他代码")

定义模块使用者

1
2
3
4
5
# 模块B:module_b.py
import module_a # 导入模块A

print(f"模块A的__name__:{module_a.__name__}") # 输出:module_a
module_a.func_a() # 执行模块A的函数,但不会执行模块A的自测代码

默认模块名称会在被导入时改名

4 dir()函数与模块探索

dir()内置函数用于列出对象的属性和方法,或当前作用域中的名称,返回字符串列表。

dir()是探索模块功能的重要工具:

4.1 查看模块成员

1
2
3
4
5
6
7
8
9
10
11
12
import math_tools
import math

# 浏览math成员
print(dir(math))

# 查看模块的所有成员(返回字符串列表)
members = dir(math_tools)
print("math_tools模块成员:")
for member in members:
if not member.startswith("_"): # 过滤私有成员
print(f" {member}")

4.2 查看对象属性

1
2
3
4
5
6
7
from math_tools import add

# 查看函数对象的属性
print("add函数的属性:")
print(f" 函数名:{add.__name__}")
print(f" 文档字符串:{add.__doc__}")
print(f" 参数注解:{add.__annotations__}")

4.3 无参数使用与域归纳总结

无参数时,查看当前作用域的所有名称

1
2
3
4
5
6
7
8
9
import math_tools

def test_func():
pass

print("当前作用域的名称:")
for name in dir():
if not name.startswith("_"):
print(f" {name}") # 输出:math_tools、test_func等

打印结果解释:

  • 模块级特殊属性
    __name__:当前模块的名称,对于主程序通常是’main
    __file__:当前模块的文件路径
    __doc__:模块的文档字符串(docstring)
    __package__:模块所属的包名
    __cached__:模块编译后的缓存文件路径(.pyc文件)
  • 模块加载相关
    __loader__:负责加载此模块的对象
    __spec__:模块的规格说明对象,包含模块的元数据
    __builtins__:包含所有内置函数和异常的模块(如print、len等)
  • 其他属性
    __annotations__:存储模块级别变量的类型注解
    math_tools:导入的自定义模块
    test_func:用户定义的函数

5 包(工程化组织)

包是 Python 中用于组织多个关联模块的目录结构,是模块的命名空间指定,核心特征是:

  • 本质是包含__init__.py文件的文件夹__init__.py可为空,也可执行包初始化或设置__all__
  • 用于将多个功能相关的模块归类管理
  • 支持多层嵌套(包内可包含子包)
  • 通过包名.模块名的层级结构访问成员

5.1 包的典型结构

以一个数据处理工具包data_process为例:

1
2
3
4
5
6
7
8
9
data_process/             # 顶级包
__init__.py # 包初始化文件(可空)
io_tools.py # 输入输出模块(文件读写)
clean_tools.py # 数据清洗模块(去重、缺失值处理)
analysis_tools.py # 数据分析模块(统计、计算)
utils/ # 子包(辅助工具)
__init__.py
format_tools.py # 格式转换工具
validate_tools.py # 数据验证工具

5.2 __init__.py的作用

__init__.py的实际使用场景并不多,几乎不使用,通常为空,或构建工具默认创建,这里简单了解

  1. 标识该目录为 Python 包
  2. 包初始化时执行的代码(如初始化配置、导入子模块)
  3. 定义__all__控制from...import *导入的子模块
  4. 暴露包的公共接口(简化外部导入)

举例:

对应如下包结构的 __init__.py 包初始化文件,包含导入语句和包元数据

mypackage/
├── __init__.py
├── module1.py
└── module2.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
"""
包初始化文件
用于将目录标记为Python包
"""

# 导入子模块
from .module1 import ClassA, function_b
from .module2 import ClassC

# 定义包级别的变量
__version__ = "1.0.0"
__author__ = "it_nst"

# 控制 from package import * 的行为
__all__ = [
"ClassA",
"function_b",
"ClassC"
]

# 包初始化代码
print("Package initialized successfully")

6 创建包

基本步骤提示:

  1. 创建包文件夹
  2. 在包文件夹下创建__init__.py文件
  3. 添加模块文件(xx.pyyy.py等)
  4. 按需创建子包

在 PyCharm 中创建形状工具包 graphic,含__init__.py空文件即可、circle.pyrectangle.py 文件

结构如下:

1
2
3
4
5
6
graphic/              #形状工具包 
__init__.py
circle.py #圆形模块
rectangle.py #矩形模块
src/
main.py # 包外测试文件

圆形模块circle.py 代码:

1
2
3
4
5
6
7
8
9
10
11
"""circle.py:圆形相关功能"""
radius = 10
PI = 3.1415926

def area(radius):
"""计算圆形面积"""
return PI * radius * radius

def perimeter(radius):
"""计算圆形周长"""
return 2 * PI * radius

矩形模块rectangle.py 代码:

1
2
3
4
5
6
7
8
9
10
11
"""rectangle.py:矩形相关功能"""
rectangle_width = 10
rectangle_height = 10

def area(width, height):
"""计算矩形面积"""
return width * height

def perimeter(width, height):
"""计算矩形周长"""
return 2 * (width + height)

7 导入包

7.1 导入包内模块一(包名.模块名)

从包中导入模块,通过 “包名.模块名.成员名” 访问。

  1. 语法:import 包名.模块名 [as 别名]

  2. 案例:

1
2
3
4
import graphic.circle

# 访问模块成员
print(graphic.circle.area(10)) # 输出314.15926

7.2 导入包内的模块二(from包名import模块名)

从包中导入模块,通过 “模块名.成员名” 访问。

  1. 语法:from 包名 import 模块名 [as 别名]
  2. 案例:
1
2
3
from graphic import circle

print(circle.area(10)) # 输出314.15926

7.3 导入包内模块的成员

直接导入包中模块的成员,直接通过成员名访问。

  1. 语法:from 包名.模块名 import 成员名 [as 别名]
  2. 案例:
1
2
3
from graphic.circle import area

print(area(10)) # 输出314.15926

7.4 包的全部导入

需在包的__init__.py中设置__all__,指定允许导入的模块。

__init__.py 文件修改:

1
__all__ = ["circle"]  # 仅允许导入circle模块

使用from xx import *的方式全部导入

1
2
3
4
from graphic import *

print(circle.area(10)) # 输出314.15926
# print(rectangle.area(10, 20)) # 报错:NameError: name 'rectangle' is not defined

7.5 另一个完成案例

7.5.1 手动创建包(以data_process为例)

  1. 创建文件夹data_process
  2. 在该文件夹下创建__init__.py文件
  3. 添加模块文件(io_tools.pyclean_tools.py等)
  4. 按需创建子包(如utils文件夹及内部文件)

7.5.2 包内模块实现示例

  1. io_tools.py(文件读写功能)
1
2
3
4
5
6
7
8
9
10
11
"""数据输入输出模块"""

def read_txt(file_path: str, encoding: str = "utf-8") -> list[str]:
"""读取txt文件,返回行列表"""
with open(file_path, "r", encoding=encoding) as f:
return [line.strip() for line in f]

def write_txt(data: list[str], file_path: str, encoding: str = "utf-8") -> None:
"""将列表数据写入txt文件"""
with open(file_path, "w", encoding=encoding) as f:
f.write("\n".join(data))
  1. clean_tools.py(数据清洗功能)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"""数据清洗模块"""

def remove_duplicates(data: list) -> list:
"""去除列表中的重复元素,保持顺序"""
seen = set()
result = []
for item in data:
if item not in seen:
seen.add(item)
result.append(item)
return result

def drop_null(data: list) -> list:
"""去除列表中的空值(None、空字符串、空列表等)"""
return [item for item in data if item]
  1. __init__.py(包初始化)
1
2
3
4
5
6
7
8
9
10
11
"""数据处理工具包"""

# 定义包的版本
__version__ = "1.0.0"

# 控制from data_process import * 导入的子模块
__all__ = ["io_tools", "clean_tools", "analysis_tools"]

# 暴露公共接口(简化外部导入)
from .io_tools import read_txt, write_txt
from .clean_tools import remove_duplicates, drop_null

7.5.3 导入包(多种方式)

7.5.3.1 导入包内模块
1
2
3
4
5
6
7
8
9
10
11
# 方式1:导入包内模块,通过"包.模块.成员"访问
import data_process.io_tools

data = data_process.io_tools.read_txt("test.txt")
print(data)

# 方式2:导入模块并指定别名
import data_process.clean_tools as ct

clean_data = ct.remove_duplicates([1, 2, 2, 3, 3, 3])
print(clean_data) # 输出:[1, 2, 3]
7.5.3.2 直接导入包的公共接口

借助__init__.py的配置,可直接导入包暴露的成员:

1
2
3
4
5
6
7
8
9
10
11
# 直接导入包的公共函数(无需层级访问)
from data_process import read_txt, remove_duplicates

# 读取文件
lines = read_txt("test.txt")
print("原始数据:", lines)

# 清洗数据
clean_lines = remove_duplicates(lines)
clean_lines = drop_null(clean_lines)
print("清洗后数据:", clean_lines)
7.5.3.3 导入子包成员
1
2
3
4
5
6
# 导入子包的模块
from data_process.utils import format_tools

# 使用子包模块的功能
formatted_data = format_tools.format_date("20240101")
print(formatted_data)
7.5.3.4 全部导入包
1
2
3
4
5
6
7
# 导入包的所有公共子模块(由__all__指定)
from data_process import *

# 使用导入的模块
data = io_tools.read_txt("test.txt")
clean_data = clean_tools.drop_null(data)
print(clean_data)

8 常用标准库(包)

Python 标准库是内置的功能集合,无需安装即可使用,以下是高频使用场景及示例:

库名 核心功能 实战示例
os 操作系统接口(文件 / 目录操作) 遍历目录、创建文件夹
sys 系统参数与函数 命令行参数、退出程序
datetime 日期时间处理 时间格式化、日期计算
math 数学运算 三角函数、对数计算
random 随机数生成 随机抽样、打乱列表
re 正则表达式 文本匹配、提取、替换
json JSON 数据处理 序列化、反序列化
collections 高级容器 有序字典、计数器

更多的包总汇

名称 说明
os 操作系统接口(文件操作、路径处理等)
sys 系统相关参数和函数(命令行参数、系统路径等)
time 时间访问和转换
datetime 日期和时间处理类
math 数学函数(三角函数、对数等)
random 生成伪随机数
re 正则表达式匹配
json JSON 数据编码与解码
collections 专门化容器(有序字典、计数器等)
functools 高阶函数和可调用对象操作
hashlib 安全哈希与消息摘要
urllib URL 处理(请求发送、解析等)
smtplib SMTP 协议客户端(邮件发送)
zlib/gzip/bz2 数据压缩与解压
multiprocessing/threading 进程与线程管理
copy 深浅拷贝操作
socket 网络编程接口
shutil 高级文件操作(拷贝、删除等)
glob 路径名模式匹配

更多标准库可参考Python 官方文档

常用库的使用

1. os库:目录遍历与文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import os

# 遍历指定目录下的所有txt文件
def find_txt_files(dir_path: str) -> list[str]:
txt_files = []
for root, dirs, files in os.walk(dir_path):
for file in files:
if file.endswith(".txt"):
# 获取文件的绝对路径
abs_path = os.path.abspath(os.path.join(root, file))
txt_files.append(abs_path)
return txt_files

# 示例:查找当前目录下的所有txt文件
txt_list = find_txt_files(".")
print("找到的txt文件:")
for file in txt_list:
print(f" {file}")

2. datetime库:时间处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from datetime import datetime, timedelta

# 获取当前时间
now = datetime.now()
print("当前时间:", now.strftime("%Y-%m-%d %H:%M:%S"))

# 计算3天后的日期
future_date = now + timedelta(days=3)
print("3天后:", future_date.strftime("%Y-%m-%d"))

# 解析字符串为时间对象
date_str = "2024-01-01"
date_obj = datetime.strptime(date_str, "%Y-%m-%d")
print("解析后的时间:", date_obj)

3. json库:数据序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import json

# 字典转JSON字符串(序列化)
data = {
"name": "张三",
"age": 25,
"is_student": False,
"hobbies": ["reading", "coding"]
}
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print("JSON字符串:")
print(json_str)

# JSON字符串转字典(反序列化)
json_str = '''
{
"name": "李四",
"age": 30,
"is_student": true
}
'''
# json_str = '{"name": "李四", "age": 30, "is_student": true}'
data = json.loads(json_str)
print("\n反序列化后:", data["name"], data["age"])

9 第三方库管理

第三方库是社区开发的扩展功能包(如requestspandas),需通过包管理工具安装。

9.1 pip 命令方式

pip 是 Python 包管理工具,支持查找、下载、安装、卸载第三方包,默认源为 PyPI,可指定国内源提速。

国内源地址:

pip 常用命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 升级pip    --upgrade代表最新版本
pip install --upgrade pip

# 查看已安装包
pip list

# 安装包(默认源)
pip install 包名 包名2 包名3

# 安装库(指定版本)
pip install [--upgrade] 包名==x.xx.xx

# 卸载包
pip uninstall 包名

# 查看库详情(版本、依赖等)
pip show 包名

# 临时使用国内源安装
pip install -i http://mirrors.aliyun.com/pypi/simple/ 包名

# 永久设置国内源
pip config set global.index-url http://mirrors.aliyun.com/pypi/simple/

安装 requests 包示例:

1
2
3
4
5
# 安装
pip install -i https://mirrors.aliyun.com/pypi/simple/ requests

# 验证安装
pip list | findstr requests

9.2 PyCharm 中管理第三方库

  1. Settings中找到Python Interpreter(不同IDE版本设置位置不同),可以查看到目前安装的第三方包

    1765872653099

  2. 点击 “+” 号,搜索要安装的包(如 requests);

    1765872679364

  3. 点击 “Install Package” 完成安装。

说明:PyCharm 安装的包会存放在当前项目的虚拟环境中(如D:\dev\workspace\工程名称\.venv\Lib\site-packages)。

9.3 requirements.txt文件管理

参见py概述.md#3.2.2 一些常规的PyCharm设置

9.4 虚拟环境(隔离项目依赖)

不同项目可能依赖不同版本的库,使用虚拟环境可避免冲突:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建虚拟环境(venv为环境名称)
python -m venv venv

# 激活虚拟环境(Windows)
venv\Scripts\activate

# 激活虚拟环境(Mac/Linux)
source venv/bin/activate

# 激活后安装的库仅在当前环境生效
pip install requests

# 退出虚拟环境
deactivate

以上动作在pycharm创建项目时均会默认生成虚拟环境,所以无需手动,激活虚拟环境时,前边会有(.venv)提示

如果使用其他编辑工具打开,如命令行、cursor等集成化弱的IDE,则需要手动打开虚拟化搭建环境

1765873098351

10 打包自定义库(发布与分享)

将自己的模块 / 包打包为可安装的库,方便在多个项目中使用或分享给他人。

10.1 打包准备

安装打包工具:pip install setuptools wheel

  • setuptools 库作用(本案例使用)
    包构建工具:用于创建和管理Python包的分发版本
    依赖管理:处理包之间的依赖关系声明和解析
    入口点定义:允许定义命令行脚本和插件入口
    元数据管理:管理包的版本、作者、许可证等信息
  • wheel 库作用
    现代分发格式:提供 .whl 格式的预编译包分发机制
    快速安装:相比源码分发,wheel包安装速度更快
    跨平台兼容:支持纯Python包和平台特定的二进制包
    标准化格式:遵循PEP 427标准的分发格式
1
2
#这里使用setuptools
pip install --upgrade setuptools

项目结构整理

1
2
3
4
5
6
7
helloworld/              # 项目根目录
graphic/ # 自定义包(要打包的核心代码)
__init__.py
circle.py
rectangle.py
setup.py # 打包配置文件
README.md # 项目说明文档

10.2 编写setup.py

与 graphic 包同级创建setup.py文件

1
2
3
4
5
6
7
from setuptools import setup

setup(
name="graphic", # 包名
version="1.0", # 版本
py_modules=["graphic.circle", "graphic.rectangle"], # 需打包的模块
)

10.3 打包

1
2
3
4
5
# 生成构建文件
python setup.py build

# 生成压缩包(.tar.gz格式)
python setup.py sdist

构建及打包后的文件结构:其中graphic-1.0.tar.gz就是可以被安装的资源库了

1765876746214

10.4 安装

  • 命令安装

安装自定义包:

1
2
# 通过pip安装
pip install 路径/dist/graphic-1.0.tar.gz

安装成功:

1765876615433

  • PyCharm 中安装:

1765877020146