Python编程教程
目录
1. Python概述
1.1 Python简介
Python是一种解释型、面向对象、动态数据类型的高级程序设计语言。它由Guido van Rossum于1989年圣诞节期间创建,并在1991年首次公开发行。Python的设计哲学强调代码的可读性和简洁性,具有丰富和强大的库,常被昵称为“胶水语言”,能够把其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。
1.2 Python的主要特点
- 简单易学:Python有相对较少的关键字,结构简单,语法清晰,学习起来更加简单。
- 可移植性:Python程序可以在各种平台上运行,包括Windows、Linux、Mac OS等,无需修改代码。
- 解释性:Python不需要编译成二进制代码,可以直接从源代码运行程序。
- 面向对象:Python支持面向对象的编程方式,支持类、继承和多态等特性。
- 可扩展性:部分Python程序可以通过C/C++进行编写,然后在Python程序中调用。
- 丰富的库:Python拥有大量的标准库和第三方库,可以用于各种领域的开发。
- 动态类型:在Python中,不需要声明变量的类型,变量的类型会根据赋值自动确定。
- 自动内存管理:Python具有自动内存管理功能,开发者不需要手动管理内存。
1.3 Python的应用领域
- Web开发:使用Django、Flask等框架开发网站和Web应用。
- 数据科学:使用NumPy、Pandas、Matplotlib等库进行数据分析和可视化。
- 人工智能:使用TensorFlow、PyTorch等框架开发机器学习和深度学习模型。
- 自动化脚本:编写脚本自动化处理各种任务,如文件处理、系统管理等。
- 游戏开发:使用Pygame等库开发简单的游戏。
- 网络爬虫:使用Scrapy、Requests等库爬取网页数据。
- 桌面应用:使用Tkinter、PyQt等库开发桌面应用程序。
- 科学计算:使用SciPy等库进行科学计算和数值分析。
2. Python开发环境搭建
2.1 Python的下载与安装
- 访问Python官方网站:www.python.org/
- 下载Python安装包:根据操作系统选择合适的版本(Windows/macOS/Linux)
- 安装Python:
- Windows:运行安装程序,勾选"Add Python to PATH"选项,然后点击"Install Now"。
- macOS:运行安装程序,按照提示完成安装。
- Linux:大多数Linux发行版已经预装了Python,可以通过命令
python3 --version检查版本,如需安装最新版本,可以使用包管理器(如apt、yum等)。
2.2 验证Python安装
安装完成后,可以通过以下命令验证Python是否安装成功:
# 在命令行中执行
python --version # Python 2.x版本
python3 --version # Python 3.x版本
2.3 Python开发工具
2.3.1 IDLE
IDLE是Python自带的集成开发环境,简单易用,适合初学者使用。
2.3.2 PyCharm
PyCharm是JetBrains公司开发的Python集成开发环境,具有强大的功能,如代码补全、语法高亮、调试、版本控制等。PyCharm分为社区版(免费)和专业版(付费)。
2.3.3 VS Code
VS Code是Microsoft开发的轻量级代码编辑器,可以通过安装Python扩展支持Python开发。
2.3.4 Jupyter Notebook
Jupyter Notebook是一个交互式计算环境,可以创建包含代码、文本、图像等的文档,适合数据科学和机器学习开发。
2.4 创建第一个Python程序
使用IDLE创建并运行第一个Python程序:
- 打开IDLE
- 点击"File" -> "New File"创建一个新文件
- 在文件中输入以下代码:
print("Hello, World!") - 点击"File" -> "Save"保存文件,文件名可以为"hello.py"
- 点击"Run" -> "Run Module"运行程序,或者按F5键
- 在IDLE的Shell窗口中会显示输出结果:
Hello, World!
3. Python基础语法
3.1 注释
Python中的注释使用#符号,从#开始到行尾的内容都是注释。
# 这是单行注释
print("Hello, World!") # 这也是注释,在代码后面
"""
这是多行注释
可以写多行内容
"""
3.2 缩进
Python使用缩进来表示代码块,而不是使用大括号。缩进的空格数可以由开发者决定,但同一个代码块中的缩进必须一致。
if 5 > 2:
print("Five is greater than two!") # 正确的缩进
print("This is also inside the if block")
if 5 > 2:
print("Five is greater than two!") # 错误:没有缩进
3.3 行与缩进
- 行长度:建议每行代码不超过80个字符。
- 行连接:如果一行代码太长,可以使用反斜杠
\连接多行代码。
total = item_one + \
item_two + \
item_three
- 括号内的行连接:在括号(小括号、中括号、大括号)内,可以直接换行,不需要使用反斜杠。
total = (item_one +
item_two +
item_three)
3.4 语句分隔
Python中,分号;可以用于在同一行中分隔多个语句。
print("Hello"); print("World")
3.5 标识符
Python中的标识符是用于命名变量、函数、类、模块等的名称。标识符的命名规则如下:
- 标识符由字母、数字、下划线组成,但不能以数字开头。
- 标识符区分大小写,如
myVar和myvar是不同的标识符。 - 不能使用Python的关键字作为标识符。
- 建议使用下划线命名法(snake_case),如
my_variable。
3.6 Python关键字
Python中的关键字是具有特殊含义的保留字,不能作为标识符使用。以下是Python 3.8的关键字:
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
4. 数据类型与变量
4.1 变量
在Python中,变量不需要声明类型,直接赋值即可创建变量。
x = 5 # 创建一个整数变量x,赋值为5
y = "Hello" # 创建一个字符串变量y,赋值为"Hello"
4.2 数据类型
Python中的数据类型可以分为以下几类:
4.2.1 基本数据类型
- 整数(int):表示整数,如
5、-3、0。 - 浮点数(float):表示小数,如
3.14、-0.5。 - 布尔值(bool):表示真或假,只有
True和False两个值。 - 复数(complex):表示复数,如
3+4j。
# 整数
x = 10
print(type(x)) # <class 'int'>
# 浮点数
y = 3.14
print(type(y)) # <class 'float'>
# 布尔值
z = True
print(type(z)) # <class 'bool'>
# 复数
w = 3 + 4j
print(type(w)) # <class 'complex'>
4.2.2 序列类型
- 字符串(str):表示文本,使用单引号或双引号括起来,如
"Hello"、'World'。 - 列表(list):表示有序的可变元素集合,使用方括号括起来,如
[1, 2, 3]。 - 元组(tuple):表示有序的不可变元素集合,使用圆括号括起来,如
(1, 2, 3)。
# 字符串
s = "Hello, World!"
print(type(s)) # <class 'str'>
# 列表
l = [1, 2, 3, "apple", "banana"]
print(type(l)) # <class 'list'>
# 元组
t = (1, 2, 3, "apple", "banana")
print(type(t)) # <class 'tuple'>
4.2.3 集合类型
- 集合(set):表示无序的唯一元素集合,使用大括号括起来,如
{1, 2, 3}。 - 冻结集合(frozenset):表示不可变的集合,使用
frozenset()函数创建,如frozenset({1, 2, 3})。
# 集合
s = {1, 2, 3, 3, 4}
print(s) # {1, 2, 3, 4}(自动去重)
print(type(s)) # <class 'set'>
# 冻结集合
fs = frozenset({1, 2, 3})
print(type(fs)) # <class 'frozenset'>
4.2.4 映射类型
- 字典(dict):表示键值对的集合,使用大括号括起来,键和值之间用冒号分隔,如
{"name": "Alice", "age": 25}。
# 字典
d = {"name": "Alice", "age": 25, "city": "New York"}
print(type(d)) # <class 'dict'>
4.3 类型转换
Python提供了多种类型转换函数,可以将一种数据类型转换为另一种数据类型。
# 转换为整数
x = int(3.14) # 3
print(x)
# 转换为浮点数
y = float(5) # 5.0
print(y)
# 转换为字符串
z = str(123) # "123"
print(z)
# 转换为列表
l = list("Hello") # ['H', 'e', 'l', 'l', 'o']
print(l)
# 转换为元组
t = tuple([1, 2, 3]) # (1, 2, 3)
print(t)
# 转换为集合
s = set([1, 2, 3, 3]) # {1, 2, 3}
print(s)
# 转换为字典
d = dict([("name", "Alice"), ("age", 25)]) # {"name": "Alice", "age": 25}
print(d)
5. 控制流语句
5.1 条件语句(if-elif-else)
条件语句用于根据条件执行不同的代码块。
# 简单的if语句
x = 5
if x > 0:
print("x is positive")
# if-else语句
x = -5
if x > 0:
print("x is positive")
else:
print("x is not positive")
# if-elif-else语句
x = 0
if x > 0:
print("x is positive")
elif x < 0:
print("x is negative")
else:
print("x is zero")
5.2 循环语句
5.2.1 for循环
for循环用于遍历序列(如列表、元组、字符串)或其他可迭代对象。
# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
# 遍历字符串
for char in "Hello":
print(char)
# 使用range()函数
for i in range(5):
print(i) # 输出0, 1, 2, 3, 4
for i in range(2, 10, 2):
print(i) # 输出2, 4, 6, 8
5.2.2 while循环
while循环用于在条件为真时重复执行代码块。
# 基本的while循环
i = 0
while i < 5:
print(i)
i += 1 # 不要忘记递增i,否则会无限循环
# 使用break语句退出循环
i = 0
while i < 10:
print(i)
if i == 5:
break
i += 1
# 使用continue语句跳过当前循环
i = 0
while i < 10:
i += 1
if i % 2 == 0:
continue
print(i) # 输出1, 3, 5, 7, 9
5.2.3 循环中的else子句
在Python中,循环可以有一个else子句,当循环正常结束(没有被break中断)时执行。
# for循环中的else
for i in range(5):
print(i)
else:
print("Loop finished normally")
# while循环中的else
i = 0
while i < 5:
print(i)
i += 1
else:
print("Loop finished normally")
# 被break中断的循环不会执行else子句
for i in range(5):
print(i)
if i == 2:
break
else:
print("Loop finished normally") # 不会执行
6. 函数
6.1 函数的定义与调用
函数是一段组织好的、可重复使用的代码块,用于执行特定的任务。在Python中,使用def关键字定义函数。
# 定义一个简单的函数
def greet():
"""这是一个问候函数"""
print("Hello, World!")
# 调用函数
greet() # 输出:Hello, World!
6.2 函数参数
函数可以接受参数,以便在调用时传递数据。
# 带参数的函数
def greet(name):
"""问候指定的人"""
print(f"Hello, {name}!")
# 调用函数并传递参数
greet("Alice") # 输出:Hello, Alice!
greet("Bob") # 输出:Hello, Bob!
# 带多个参数的函数
def add(a, b):
"""计算两个数的和"""
return a + b
# 调用函数并获取返回值
result = add(3, 5)
print(result) # 输出:8
6.3 默认参数
可以为函数参数设置默认值,当调用函数时没有传递该参数,则使用默认值。
# 带默认参数的函数
def greet(name, greeting="Hello"):
"""问候指定的人,默认问候语为Hello"""
print(f"{greeting}, {name}!")
# 调用函数
greet("Alice") # 输出:Hello, Alice!
greet("Bob", "Hi") # 输出:Hi, Bob!
6.4 关键字参数
使用关键字参数可以不按照参数定义的顺序传递参数。
# 使用关键字参数
def greet(name, age):
"""问候并显示年龄"""
print(f"Hello, {name}! You are {age} years old.")
# 按照顺序传递参数
greet("Alice", 25) # 输出:Hello, Alice! You are 25 years old.
# 使用关键字参数传递
greet(age=25, name="Alice") # 输出:Hello, Alice! You are 25 years old.
6.5 可变参数
可以使用*args接收任意数量的位置参数,使用**kwargs接收任意数量的关键字参数。
# 使用*args接收任意数量的位置参数
def add(*args):
"""计算任意数量的数的和"""
total = 0
for num in args:
total += num
return total
# 调用函数
result = add(1, 2, 3, 4, 5)
print(result) # 输出:15
# 使用**kwargs接收任意数量的关键字参数
def greet(**kwargs):
"""使用关键字参数问候"""
if "name" in kwargs:
print(f"Hello, {kwargs['name']}!")
if "age" in kwargs:
print(f"You are {kwargs['age']} years old.")
# 调用函数
greet(name="Alice", age=25) # 输出:Hello, Alice! You are 25 years old.
6.6 函数返回值
使用return语句可以从函数中返回值。如果函数没有return语句,则默认返回None。
# 返回单个值
def square(x):
"""计算一个数的平方"""
return x * x
result = square(5)
print(result) # 输出:25
# 返回多个值
def get_name_and_age():
"""返回姓名和年龄"""
name = "Alice"
age = 25
return name, age
name, age = get_name_and_age()
print(name, age) # 输出:Alice 25
6.7 匿名函数(lambda)
使用lambda关键字可以创建匿名函数,匿名函数可以接受任意数量的参数,但只能有一个表达式。
# 创建匿名函数
square = lambda x: x * x
print(square(5)) # 输出:25
# 在函数中使用匿名函数
def apply_function(func, x):
"""应用函数到x"""
return func(x)
result = apply_function(lambda x: x + 1, 5)
print(result) # 输出:6
# 排序时使用匿名函数
fruits = [("apple", 3), ("banana", 1), ("cherry", 2)]
fruits.sort(key=lambda fruit: fruit[1])
print(fruits) # 输出:[("banana", 1), ("cherry", 2), ("apple", 3)]
6.8 函数作用域
在Python中,变量的作用域分为以下几种:
- 局部作用域(Local):在函数内部定义的变量,只能在函数内部访问。
- 闭包作用域(Enclosing):在嵌套函数中,外层函数的变量可以被内层函数访问。
- 全局作用域(Global):在模块级定义的变量,可以在模块内的任何地方访问。
- 内置作用域(Built-in):Python内置的变量和函数,如
print、len等。
# 全局变量
global_var = "I am global"
# 局部变量
def func():
local_var = "I am local"
print(local_var) # 可以访问局部变量
print(global_var) # 可以访问全局变量
func()
print(global_var) # 可以访问全局变量
# print(local_var) # 错误:无法访问局部变量
# 使用global关键字在函数内部修改全局变量
def modify_global():
global global_var
global_var = "I am modified"
print(global_var) # 输出:I am global
modify_global()
print(global_var) # 输出:I am modified
7. 面向对象编程
7.1 类的定义与实例化
类是面向对象编程的基本概念,用于定义对象的属性和方法。在Python中,使用class关键字定义类。
# 定义一个简单的类
class Person:
"""人"""
# 类属性
species = "Homo sapiens"
# 初始化方法
def __init__(self, name, age):
# 实例属性
self.name = name
self.age = age
# 实例方法
def greet(self):
"""问候"""
print(f"Hello, my name is {self.name}. I am {self.age} years old.")
# 实例化类
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
# 访问实例属性
print(person1.name) # 输出:Alice
print(person1.age) # 输出:25
# 调用实例方法
person1.greet() # 输出:Hello, my name is Alice. I am 25 years old.
# 访问类属性
print(person1.species) # 输出:Homo sapiens
print(Person.species) # 输出:Homo sapiens
7.2 继承
继承是面向对象编程的重要特性,允许一个类继承另一个类的属性和方法。在Python中,定义子类时将父类作为参数传递。
# 定义父类
class Animal:
"""动物"""
def __init__(self, name):
self.name = name
def eat(self):
"""吃"""
print(f"{self.name} is eating.")
# 定义子类,继承自Animal
class Dog(Animal):
"""狗"""
def bark(self):
"""叫"""
print(f"{self.name} is barking.")
# 定义子类,继承自Animal
class Cat(Animal):
"""猫"""
def meow(self):
"""喵"""
print(f"{self.name} is meowing.")
# 实例化子类
dog = Dog("Buddy")
cat = Cat("Kitty")
# 调用继承的方法
dog.eat() # 输出:Buddy is eating.
cat.eat() # 输出:Kitty is eating.
# 调用子类自己的方法
dog.bark() # 输出:Buddy is barking.
cat.meow() # 输出:Kitty is meowing.
7.3 多态
多态是面向对象编程的另一个重要特性,允许不同类的对象对同一消息作出不同的响应。
# 定义父类
class Shape:
"""形状"""
def area(self):
"""计算面积"""
pass
# 定义子类
class Rectangle(Shape):
"""矩形"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
"""计算矩形面积"""
return self.width * self.height
# 定义子类
class Circle(Shape):
"""圆形"""
def __init__(self, radius):
self.radius = radius
def area(self):
"""计算圆形面积"""
import math
return math.pi * self.radius ** 2
# 多态的使用
def print_area(shape):
"""打印形状的面积"""
print(f"Area: {shape.area()}")
# 创建不同的形状对象
rectangle = Rectangle(3, 4)
circle = Circle(5)
# 调用相同的函数,但输出不同的结果
print_area(rectangle) # 输出:Area: 12
print_area(circle) # 输出:Area: 78.53981633974483
7.4 封装
封装是面向对象编程的第三个重要特性,用于隐藏对象的内部状态,只提供有限的接口供外部访问。在Python中,可以使用下划线来表示私有属性和方法。
# 封装的例子
class BankAccount:
"""银行账户"""
def __init__(self, account_number, balance):
# 公共属性
self.account_number = account_number
# 私有属性(使用双下划线开头)
self.__balance = balance
# 公共方法
def deposit(self, amount):
"""存款"""
if amount > 0:
self.__balance += amount
print(f"Deposited ${amount}. New balance: ${self.__balance}")
else:
print("Amount must be positive.")
def withdraw(self, amount):
"""取款"""
if 0 < amount <= self.__balance:
self.__balance -= amount
print(f"Withdrew ${amount}. New balance: ${self.__balance}")
else:
print("Insufficient funds or invalid amount.")
def get_balance(self):
"""获取余额"""
return self.__balance
# 创建账户对象
account = BankAccount("123456789", 1000)
# 访问公共属性
print(account.account_number) # 输出:123456789
# 调用公共方法
account.deposit(500) # 输出:Deposited $500. New balance: $1500
account.withdraw(200) # 输出:Withdrew $200. New balance: $1300
# 获取余额
print(account.get_balance()) # 输出:1300
# 尝试直接访问私有属性(会出错)
# print(account.__balance) # 错误:AttributeError
8. 模块与包
8.1 模块的定义与导入
模块是一个包含Python代码的文件,文件名就是模块名加上.py扩展名。使用import语句可以导入模块。
# 导入整个模块
import math
# 使用模块中的函数
print(math.sqrt(16)) # 输出:4.0
print(math.pi) # 输出:3.141592653589793
# 导入模块的一部分
from math import sqrt, pi
# 使用导入的函数
print(sqrt(25)) # 输出:5.0
print(pi) # 输出:3.141592653589793
# 给模块起别名
import math as m
# 使用别名
print(m.sqrt(36)) # 输出:6.0
# 导入模块中的所有内容(不推荐)
from math import *
# 使用导入的内容
print(sqrt(49)) # 输出:7.0
8.2 包的定义与导入
包是一个包含多个模块的目录,目录中必须包含一个名为__init__.py的文件(可以是空文件)。
my_package/
__init__.py
module1.py
module2.py
# 导入包中的模块
import my_package.module1
# 使用模块中的函数
my_package.module1.function1()
# 从包中导入模块
from my_package import module2
# 使用模块中的函数
module2.function2()
# 从包中导入模块并起别名
from my_package import module1 as m1
# 使用别名
m1.function1()
8.3 标准库模块
Python提供了丰富的标准库模块,以下是一些常用的标准库模块:
- os:提供与操作系统交互的功能。
- sys:提供与Python解释器交互的功能。
- math:提供数学计算功能。
- random:提供随机数生成功能。
- datetime:提供日期和时间处理功能。
- csv:提供CSV文件处理功能。
- json:提供JSON数据处理功能。
- re:提供正则表达式处理功能。
- urllib:提供URL处理功能。
- argparse:提供命令行参数解析功能。
9. 文件操作
9.1 文件的打开与关闭
使用open()函数可以打开文件,使用close()方法可以关闭文件。
# 打开文件
file = open("test.txt", "w") # "w"表示写入模式
# 写入内容
file.write("Hello, World!\n")
file.write("This is a test file.")
# 关闭文件
file.close()
# 使用with语句自动关闭文件(推荐)
with open("test.txt", "r") as file: # "r"表示读取模式
content = file.read()
print(content)
9.2 文件打开模式
Python中的文件打开模式包括:
- r:只读模式(默认)。
- w:写入模式,如果文件存在则覆盖,如果文件不存在则创建。
- a:追加模式,如果文件存在则在文件末尾追加内容,如果文件不存在则创建。
- x:独占创建模式,如果文件已经存在则抛出错误。
- b:二进制模式,与其他模式结合使用,如"rb"、"wb"等。
- t:文本模式(默认),与其他模式结合使用,如"rt"、"wt"等。
- +:读写模式,与其他模式结合使用,如"r+"、"w+"等。
9.3 文件读取
# 读取整个文件
with open("test.txt", "r") as file:
content = file.read()
print(content)
# 逐行读取
with open("test.txt", "r") as file:
for line in file:
print(line.strip()) # strip()用于去除换行符
# 读取指定数量的字符
with open("test.txt", "r") as file:
content = file.read(10) # 读取前10个字符
print(content)
# 读取所有行到列表
with open("test.txt", "r") as file:
lines = file.readlines()
print(lines)
9.4 文件写入
# 写入字符串
with open("test.txt", "w") as file:
file.write("Hello, World!\n")
file.write("This is a test file.")
# 写入列表
lines = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open("test.txt", "w") as file:
file.writelines(lines)
9.5 文件指针
文件指针用于指示当前文件的位置,可以使用tell()方法获取当前文件指针的位置,使用seek()方法移动文件指针。
with open("test.txt", "r") as file:
content = file.read(10) # 读取前10个字符
print(content)
print(file.tell()) # 输出:10(当前文件指针的位置)
file.seek(0) # 移动文件指针到文件开头
content = file.read(5)
print(content) # 输出:前5个字符
10. 异常处理
10.1 异常的概念
异常是程序运行时发生的错误,如除以零、访问不存在的文件、类型错误等。当异常发生时,程序会终止执行并显示错误信息。
10.2 try-except语句
使用try-except语句可以捕获并处理异常,避免程序终止执行。
# 基本的异常处理
try:
x = 10 / 0 # 会抛出ZeroDivisionError异常
except ZeroDivisionError:
print("Error: Division by zero!")
# 处理多种异常
try:
x = int("abc") # 会抛出ValueError异常
y = 10 / 0 # 会抛出ZeroDivisionError异常
except ValueError:
print("Error: Invalid number!")
except ZeroDivisionError:
print("Error: Division by zero!")
# 处理所有异常
try:
# 可能发生异常的代码
x = 10 / 0
except Exception as e:
print(f"Error: {e}") # 输出:Error: division by zero
# try-except-else语句
try:
x = 10 / 2
except ZeroDivisionError:
print("Error: Division by zero!")
else:
print(f"Result: {x}") # 输出:Result: 5.0
# try-except-finally语句
try:
file = open("test.txt", "r")
content = file.read()
print(content)
except FileNotFoundError:
print("Error: File not found!")
finally:
file.close() # 无论是否发生异常,都会执行finally块中的代码
10.3 异常的抛出
使用raise语句可以主动抛出异常。
def divide(x, y):
"""计算两个数的商"""
if y == 0:
raise ZeroDivisionError("Cannot divide by zero!")
return x / y
try:
result = divide(10, 0)
except ZeroDivisionError as e:
print(f"Error: {e}") # 输出:Error: Cannot divide by zero!
10.4 自定义异常
可以通过继承Exception类来创建自定义异常。
# 自定义异常类
class NegativeNumberError(Exception):
"""当输入负数时抛出的异常"""
pass
def calculate_square_root(x):
"""计算平方根"""
if x < 0:
raise NegativeNumberError("Cannot calculate square root of negative number!")
return math.sqrt(x)
try:
result = calculate_square_root(-5)
except NegativeNumberError as e:
print(f"Error: {e}") # 输出:Error: Cannot calculate square root of negative number!
11. 常用标准库
11.1 os模块
os模块提供与操作系统交互的功能。
import os
# 获取当前工作目录
cwd = os.getcwd()
print(cwd)
# 改变当前工作目录
os.chdir("/path/to/directory")
# 创建目录
os.mkdir("new_directory")
# 创建多级目录
os.makedirs("parent_directory/child_directory")
# 列出目录内容
files = os.listdir(".")
print(files)
# 删除文件
os.remove("file.txt")
# 删除目录
os.rmdir("directory")
# 删除多级目录
os.removedirs("parent_directory/child_directory")
# 重命名文件或目录
os.rename("old_name.txt", "new_name.txt")
# 判断文件或目录是否存在
print(os.path.exists("file.txt"))
# 判断是否是文件
print(os.path.isfile("file.txt"))
# 判断是否是目录
print(os.path.isdir("directory"))
# 获取文件大小
print(os.path.getsize("file.txt"))
# 获取文件的绝对路径
print(os.path.abspath("file.txt"))
11.2 sys模块
sys模块提供与Python解释器交互的功能。
import sys
# 获取命令行参数
print(sys.argv)
# 获取Python版本信息
print(sys.version)
# 获取Python路径
print(sys.path)
# 退出程序
sys.exit(0)
11.3 random模块
random模块提供随机数生成功能。
import random
# 生成0到1之间的随机浮点数
print(random.random())
# 生成指定范围内的随机整数
print(random.randint(1, 10))
# 生成指定范围内的随机浮点数
print(random.uniform(1, 10))
# 从序列中随机选择一个元素
print(random.choice([1, 2, 3, 4, 5]))
# 从序列中随机选择多个元素
print(random.sample([1, 2, 3, 4, 5], 2))
# 打乱序列
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)
print(numbers)
11.4 datetime模块
datetime模块提供日期和时间处理功能。
import datetime
# 获取当前日期和时间
now = datetime.datetime.now()
print(now)
# 获取当前日期
today = datetime.date.today()
print(today)
# 创建日期对象
date = datetime.date(2023, 12, 25)
print(date)
# 创建时间对象
time = datetime.time(12, 30, 45)
print(time)
# 格式化日期和时间
formatted_date = now.strftime("%Y-%m-%d %H:%M:%S")
print(formatted_date)
# 解析日期和时间
parsed_date = datetime.datetime.strptime("2023-12-25 12:30:45", "%Y-%m-%d %H:%M:%S")
print(parsed_date)
# 日期和时间的计算
tomorrow = today + datetime.timedelta(days=1)
print(tomorrow)
next_hour = now + datetime.timedelta(hours=1)
print(next_hour)
11.5 json模块
json模块提供JSON数据处理功能。
import json
# 将Python对象转换为JSON字符串
python_obj = {"name": "Alice", "age": 25, "city": "New York"}
json_str = json.dumps(python_obj)
print(json_str) # 输出:{"name": "Alice", "age": 25, "city": "New York"}
# 将JSON字符串转换为Python对象
python_obj2 = json.loads(json_str)
print(python_obj2) # 输出:{"name": "Alice", "age": 25, "city": "New York"}
# 将Python对象写入JSON文件
with open("data.json", "w") as file:
json.dump(python_obj, file)
# 从JSON文件读取Python对象
with open("data.json", "r") as file:
python_obj3 = json.load(file)
print(python_obj3)
12. 高级特性
12.1 列表推导式
列表推导式用于快速创建列表。
# 创建包含1到10的平方的列表
squares = [x ** 2 for x in range(1, 11)]
print(squares) # 输出:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 创建包含1到10的偶数的平方的列表
even_squares = [x ** 2 for x in range(1, 11) if x % 2 == 0]
print(even_squares) # 输出:[4, 16, 36, 64, 100]
# 创建二维列表
matrix = [[i for i in range(3)] for j in range(3)]
print(matrix) # 输出:[[0, 1, 2], [0, 1, 2], [0, 1, 2]]
12.2 字典推导式
字典推导式用于快速创建字典。
# 创建包含1到10的平方的字典
squares_dict = {x: x ** 2 for x in range(1, 11)}
print(squares_dict) # 输出:{1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100}
# 创建包含1到10的偶数的平方的字典
even_squares_dict = {x: x ** 2 for x in range(1, 11) if x % 2 == 0}
print(even_squares_dict) # 输出:{2: 4, 4: 16, 6: 36, 8: 64, 10: 100}
12.3 集合推导式
集合推导式用于快速创建集合。
# 创建包含1到10的平方的集合
squares_set = {x ** 2 for x in range(1, 11)}
print(squares_set) # 输出:{64, 1, 4, 36, 100, 9, 16, 49, 81, 25}
# 创建包含1到10的偶数的平方的集合
even_squares_set = {x ** 2 for x in range(1, 11) if x % 2 == 0}
print(even_squares_set) # 输出:{64, 4, 36, 100, 16}
12.4 生成器表达式
生成器表达式用于创建生成器,与列表推导式类似,但使用圆括号。
# 创建生成器
squares_gen = (x ** 2 for x in range(1, 11))
print(squares_gen) # 输出:<generator object <genexpr> at 0x0000020D7C4A1F90>
# 遍历生成器
for square in squares_gen:
print(square)
# 生成器的优点:节省内存
squares_list = [x ** 2 for x in range(1000000)] # 占用大量内存
squares_gen = (x ** 2 for x in range(1000000)) # 占用很少内存
12.5 迭代器与可迭代对象
- 可迭代对象(Iterable):可以使用
for循环遍历的对象,如列表、元组、字符串、字典等。 - 迭代器(Iterator):可以使用
next()函数获取下一个元素的对象。
# 可迭代对象
fruits = ["apple", "banana", "cherry"]
# 将可迭代对象转换为迭代器
iterator = iter(fruits)
# 使用next()函数获取下一个元素
print(next(iterator)) # 输出:apple
print(next(iterator)) # 输出:banana
print(next(iterator)) # 输出:cherry
# print(next(iterator)) # 错误:StopIteration
# 自定义迭代器
class MyIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current > self.end:
raise StopIteration
else:
value = self.current
self.current += 1
return value
# 使用自定义迭代器
for num in MyIterator(1, 5):
print(num) # 输出:1, 2, 3, 4, 5
12.6 Lock 锁
简单来说,Lock(互斥锁)是 Python 多线程编程中用于保证同一时间只有一个线程能执行某段代码的工具,核心作用是解决多线程并发访问共享资源时的 "竞态条件"(Race Condition)问题。
可以用一个生活例子理解:假设多个线程是多个人,共享资源是卫生间,Lock就是卫生间的门锁 —— 一个人进去后锁上门(acquire()),其他人必须等他出来解锁(release())才能进去,避免同时占用导致混乱。
Lock 的基本使用
Lock属于threading模块,核心方法有两个:
acquire():获取锁(如果锁已被占用,则当前线程阻塞,直到锁被释放)release():释放锁(必须确保获取锁的线程才能释放,否则会报错)
示例代码(对比有无锁的区别)
import threading
import time
# 共享资源
count = 0
# 创建锁对象
lock = threading.Lock()
# 无锁的函数(会出现竞态条件)
def add_without_lock():
global count
for _ in range(100000):
# 多线程同时修改count,会导致最终结果小于预期
count += 1
# 有锁的函数(保证原子操作)
def add_with_lock():
global count
for _ in range(100000):
# 获取锁
lock.acquire()
try:
# 临界区(修改共享资源的代码)
count += 1
finally:
# 释放锁(用finally确保即使出错也会释放)
lock.release()
# 测试无锁场景
count = 0
t1 = threading.Thread(target=add_without_lock)
t2 = threading.Thread(target=add_without_lock)
t1.start()
t2.start()
t1.join()
t2.join()
print("无锁时的结果(预期200000):", count) # 结果通常小于200000
# 测试有锁场景
count = 0
t3 = threading.Thread(target=add_with_lock)
t4 = threading.Thread(target=add_with_lock)
t3.start()
t4.start()
t3.join()
t4.join()
print("有锁时的结果(预期200000):", count) # 结果一定是200000
代码解释:
- 无锁场景:两个线程同时修改
count,count += 1并非原子操作(实际分 "读取 - 加 1 - 写入" 三步),会导致部分修改被覆盖,最终结果小于预期。 - 有锁场景:通过
lock.acquire()和lock.release()包裹临界区代码,确保同一时间只有一个线程能执行count += 1,避免竞态条件。 - 用
try...finally包裹release()是最佳实践:即使临界区代码报错,也能保证锁被释放,避免 "死锁"。
- Lock 的关键特性
- 互斥性:同一时间只能有一个线程持有锁,其他线程获取锁时会阻塞。
- 非重入性:同一个线程不能多次调用
acquire()(会导致死锁),如果需要重入锁,应使用RLock(可重入锁)。 - 手动释放:必须确保
acquire()和release()成对出现,否则会导致锁一直被占用,其他线程永久阻塞。
错误示例(死锁):
lock = threading.Lock()
lock.acquire()
lock.acquire() # 同一线程再次获取锁,导致死锁,程序卡住
lock.release()
lock.release()
- Lock 的适用场景
- 多线程修改共享变量(如计数器、列表、字典)。
- 多线程操作同一文件 / 网络连接等独占资源。
- 任何需要保证 "原子操作" 的场景(原子操作:不可被中断的一个或一系列操作)。
总结
Lock(互斥锁)是 Python 多线程中解决共享资源竞争的核心工具,保证同一时间只有一个线程执行临界区代码。- 核心方法是
acquire()(获取锁)和release()(释放锁),必须成对使用,建议用try...finally确保释放。 Lock是非重入的,同一线程多次获取会导致死锁,重入场景需用RLock。
12.7 Process 进程
Process 是 Python multiprocessing 模块中用于创建和管理进程的核心类,简单来说,它是实现多进程编程的 "最小单元"—— 每个 Process 实例对应一个独立的操作系统进程,可并行执行任务,核心作用是充分利用多核 CPU 资源(解决 Python 多线程因 GIL 锁导致的性能瓶颈)。
可以用生活例子理解:
- 单进程:一个厨师在厨房独自完成洗菜、切菜、炒菜所有工作;
- 多进程:多个厨师同时在不同灶台干活,各自独立,互不干扰,整体效率更高。
- Process 的基本使用
步骤 1:导入模块
from multiprocessing import Process
import os
import time
步骤 2:定义进程执行的任务函数
# 任务函数:每个进程要执行的逻辑
def task(name, delay):
# 获取当前进程的ID和父进程ID(验证进程独立性)
print(f"子进程[{os.getpid()}]:执行任务 {name},父进程ID:{os.getppid()}")
time.sleep(delay) # 模拟耗时操作
print(f"子进程[{os.getpid()}]:任务 {name} 完成")
步骤 3:创建并启动进程
if __name__ == "__main__":
# 主进程信息
print(f"主进程ID:{os.getpid()}")
# 1. 创建Process实例
# 参数说明:
# target:进程要执行的函数
# args:传给函数的位置参数(元组形式)
# kwargs:传给函数的关键字参数(字典形式)
p1 = Process(target=task, args=("下载文件", 2))
p2 = Process(target=task, kwargs={"name": "处理数据", "delay": 1})
# 2. 启动进程(此时才真正创建操作系统进程)
p1.start()
p2.start()
# 3. 等待子进程结束(主进程阻塞,直到子进程完成)
p1.join()
p2.join()
print("所有子进程执行完毕,主进程结束")
输出结果(顺序可能因进程调度不同而变化):
主进程ID:1234
子进程[5678]:执行任务 下载文件,父进程ID:1234
子进程[9012]:执行任务 处理数据,父进程ID:1234
子进程[9012]:任务 处理数据 完成
子进程[5678]:任务 下载文件 完成
所有子进程执行完毕,主进程结束
- Process 的核心特性
(1)进程独立性
- 每个
Process对应独立的内存空间,子进程无法直接访问主进程的变量(反之亦然),数据不共享; - 进程间通信需通过管道(Pipe)、队列(Queue)、共享内存(Value/Array)等方式实现。
(2)关键方法
表格
| 方法 | 作用 |
|---|---|
start() | 启动进程(调用底层操作系统接口创建进程,自动执行 target 函数) |
join(timeout) | 等待进程结束(timeout 为可选超时时间,主进程阻塞直到子进程完成 / 超时) |
terminate() | 强制终止进程(需谨慎使用,可能导致资源未释放) |
is_alive() | 判断进程是否正在运行(返回布尔值) |
close() | 关闭进程对象,释放资源(进程结束后调用) |
(3)进程属性
if __name__ == "__main__":
p = Process(target=task, args=("测试", 1))
p.start()
print(f"进程名称:{p.name}") # 默认Process-1,可自定义name参数
print(f"进程PID:{p.pid}") # 进程ID(start()后才生成)
print(f"是否存活:{p.is_alive()}") # True
p.join()
print(f"是否存活:{p.is_alive()}") # False
p.close()
- 进程 vs 线程(核心区别)
表格
| 维度 | 进程(Process) | 线程(Thread) |
|---|---|---|
| 资源占用 | 高(独立内存 / CPU 资源) | 低(共享进程内存) |
| 并行执行 | 真正并行(利用多核 CPU) | 受 GIL 限制,仅单核伪并行 |
| 数据共享 | 不共享,需 IPC 机制 | 共享进程数据,需加锁 |
| 创建 / 销毁开销 | 大 | 小 |
| 稳定性 | 高(一个进程崩溃不影响其他) | 低(一个线程崩溃导致进程崩溃) |
- 实际应用场景
场景 1:CPU 密集型任务(多进程首选)
比如数据计算、图像处理、机器学习训练等,充分利用多核:
from multiprocessing import Process
import math
# 计算大数字的平方根(CPU密集)
def calc_sqrt(num, start, end):
result = [math.sqrt(num + i) for i in range(start, end)]
print(f"进程{os.getpid()}计算完成,结果长度:{len(result)}")
if __name__ == "__main__":
num = 1000000
# 拆分任务到4个进程(对应4核CPU)
p1 = Process(target=calc_sqrt, args=(num, 0, 250000))
p2 = Process(target=calc_sqrt, args=(num, 250000, 500000))
p3 = Process(target=calc_sqrt, args=(num, 500000, 750000))
p4 = Process(target=calc_sqrt, args=(num, 750000, 1000000))
[p.start() for p in [p1, p2, p3, p4]]
[p.join() for p in [p1, p2, p3, p4]]
场景 2:独立任务并行执行
比如同时下载多个文件、同时处理多个网络请求:
python
运行
def download_file(url, save_path):
# 模拟下载逻辑
print(f"开始下载 {url}")
time.sleep(2)
print(f"{url} 下载完成,保存到 {save_path}")
if __name__ == "__main__":
urls = [
("https://a.com/file1", "file1.txt"),
("https://b.com/file2", "file2.txt"),
("https://c.com/file3", "file3.txt")
]
# 为每个下载任务创建进程
processes = [Process(target=download_file, args=url) for url in urls]
[p.start() for p in processes]
[p.join() for p in processes]
-
注意事项
-
Windows 系统必须加
if __name__ == "__main__":Windows 没有 fork 机制,启动进程时会重新导入脚本,不加该判断会导致无限创建进程,报错崩溃。 -
避免创建过多进程:进程开销大,数量建议等于 CPU 核心数(
multiprocessing.cpu_count()可获取核心数),过多会导致调度开销大于收益。 -
子进程无法共享主进程变量:如需共享数据,使用
multiprocessing.Queue/Pipe/Manager等 IPC 工具。
Process是 Pythonmultiprocessing模块的核心类,用于创建独立的操作系统进程,实现真正的并行计算。- 核心用法:创建实例(指定
target和参数)→start()启动 →join()等待结束,适用于 CPU 密集型任务。 - 关键特性:进程独立(内存不共享)、开销大、稳定性高,与线程相比更适合充分利用多核 CPU 资源。
12.8 join() terminate() 守护进程 自定义进程
你问到的这些都是 multiprocessing.Process 中最核心的进阶用法,我会逐个用「定义+示例+核心说明」的方式讲清楚,确保新手能理解并直接上手使用。
- join() 方法
定义
join([timeout])是 Process 类的核心方法,作用是让主进程阻塞,等待指定子进程执行完毕后再继续运行(可选timeout超时时间,超时后主进程不再等待)。
- 不加
join():主进程不会等子进程,可能主进程先结束,子进程仍在后台运行; - 加
join():主进程会卡在join()位置,直到子进程完成/超时。
示例
from multiprocessing import Process
import time
def task(name, delay):
print(f"子进程 {name} 启动")
time.sleep(delay)
print(f"子进程 {name} 结束")
if __name__ == "__main__":
start = time.time()
p1 = Process(target=task, args=("A", 2))
p2 = Process(target=task, args=("B", 1))
p1.start()
p2.start()
等待子进程完成(主进程阻塞)
p1.join() 等p1完成(2秒)
p2.join() 等p2完成(此时p2已完成,无需等待)
print(f"主进程结束,总耗时:{time.time()-start:.2f}秒") 总耗时≈2秒
核心说明
join()按调用顺序等待,但若多个子进程并行,总耗时取决于最慢的那个;timeout参数:p1.join(1)表示主进程最多等1秒,超时后继续执行后续代码。
- terminate() 方法 (terminate:英文是终止的意思)
定义
terminate()用于强制终止子进程(立即杀死进程,不管任务是否完成),属于「暴力终止」,需谨慎使用。
- 终止后需调用
join()等待操作系统回收进程资源(避免僵尸进程); - 终止的进程无法恢复,且可能导致资源未释放(如文件句柄、网络连接)。
示例
from multiprocessing import Process
import time
def task():
print("子进程启动,开始无限循环...")
while True:
time.sleep(1)
print("子进程运行中...")
if __name__ == "__main__":
p = Process(target=task)
p.start()
time.sleep(3) 让子进程运行3秒
p.terminate() 强制终止子进程
p.join() 回收资源
print("子进程已被终止,主进程结束")
核心说明
- 终止后调用
p.is_alive()会返回False; - 仅用于紧急终止,优先通过「标志位」让子进程主动退出(更安全)。
- 守护进程(Daemon Process) 定义 守护进程是「后台进程」(一种依附于主进程的进程),主进程结束时,守护进程会被强制终止(不管任务是否完成),适用于无需手动回收的辅助任务(如日志收集、监控)。
- 设置方式:
p.daemon = True(必须在start()前设置); - 普通子进程:主进程结束后仍会继续运行,直到任务完成。
示例
from multiprocessing import Process
import time
守护进程任务:持续打印
def daemon_task():
while True:
print("守护进程运行中...")
time.sleep(1)
普通子进程任务
def normal_task():
time.sleep(3)
print("普通子进程完成")
if __name__ == "__main__":
守护进程
p1 = Process(target=daemon_task)
p1.daemon = True 设置为守护进程
p1.start()
普通子进程
p2 = Process(target=normal_task)
p2.start()
time.sleep(2) 主进程运行2秒后结束
print("主进程结束")
主进程结束后,p1(守护进程)被终止,p2(普通进程)仍会完成并打印
输出结果
守护进程运行中...
守护进程运行中...
主进程结束
普通子进程完成
- 进程之间不共享变量 定义 每个进程有独立的内存空间,主进程的变量修改不会影响子进程,反之亦然(核心区别于线程)。
- 原因:创建子进程时,会复制主进程的变量到子进程内存,此后两者互不干扰。
示例(验证不共享)
from multiprocessing import Process
全局变量
num = 0
def add_num():
global num
num += 10 子进程修改的是自己的num副本
print(f"子进程内num:{num}") 输出10
if __name__ == "__main__":
p = Process(target=add_num)
p.start()
p.join()
print(f"主进程内num:{num}") 输出0(未被修改)
- 进程通信:Queue 队列(推荐)
定义
multiprocessing.Queue是基于管道+锁实现的安全进程通信工具,支持多进程「生产者-消费者」模式,可跨进程传递任意可序列化对象(如列表、字典)。
- 核心方法:
put()(放入数据)、get()(取出数据,默认阻塞)、empty()(判断是否为空)。
示例(生产者+消费者)
from multiprocessing import Process, Queue
import time
生产者:向队列放数据
def producer(q):
for i in range(3):
data = f"数据{i}"
q.put(data)
print(f"生产者放入:{data}")
time.sleep(1)
消费者:从队列取数据
def consumer(q):
while True:
if not q.empty():
data = q.get()
print(f"消费者取出:{data}")
time.sleep(1)
else:
break
if __name__ == "__main__":
q = Queue() 创建队列
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start()
p1.join() 等生产者放完数据
p2.start()
p2.join()
print("通信完成")
输出结果
生产者放入:数据0
生产者放入:数据1
生产者放入:数据2
消费者取出:数据0
消费者取出:数据1
消费者取出:数据2
通信完成
- 进程通信:Pipe 管道
定义
multiprocessing.Pipe()创建双向/单向管道,返回两个连接对象(conn1、conn2),进程通过这两个对象收发数据,适用于两个进程间的高效通信。
- 双向管道(默认):两个连接对象均可读写;
- 单向管道:
Pipe(duplex=False),conn1只写,conn2只读。
示例(双向管道)
from multiprocessing import Process, Pipe
子进程1:向管道写数据
def send_data(conn):
data = ["苹果", "香蕉", "橙子"]
for item in data:
conn.send(item)
print(f"发送:{item}")
conn.close() 关闭连接
子进程2:从管道读数据
def recv_data(conn):
while True:
try:
data = conn.recv() 无数据时阻塞,连接关闭后抛EOFError
print(f"接收:{data}")
except EOFError:
break
if __name__ == "__main__":
conn1, conn2 = Pipe() 创建双向管道
p1 = Process(target=send_data, args=(conn1,))
p2 = Process(target=recv_data, args=(conn2,))
p1.start()
p2.start()
p1.join()
p2.join()
print("管道通信完成")
核心说明
- Queue 是「多进程安全」的,Pipe 需自己处理并发(推荐用 Queue);
- Pipe 速度比 Queue 快,适合两个进程间的高频通信。
- 继承 Process 类创建进程
定义
除了
Process(target=函数)的方式,还可通过继承 Process 类并重写run()方法创建自定义进程,适合复杂逻辑(如封装进程属性/方法)。
run()方法:进程启动后自动执行的逻辑(替代target参数);- 启动方式:实例化自定义类 →
start()(自动调用run())。
示例
from multiprocessing import Process
import time
自定义进程类
class MyProcess(Process):
初始化:自定义参数
def __init__(self, name, delay):
super().__init__() 必须调用父类初始化
self.name = name
self.delay = delay
重写run方法:进程执行的逻辑
def run(self):
print(f"自定义进程 {self.name} 启动")
time.sleep(self.delay)
print(f"自定义进程 {self.name} 结束")
if __name__ == "__main__":
实例化并启动
p1 = MyProcess("A", 2)
p2 = MyProcess("B", 1)
p1.start()
p2.start()
p1.join()
p2.join()
print("所有自定义进程完成")
总结
- join()/terminate():
join()让主进程等待子进程,terminate()强制终止子进程(需配join()回收资源); - 守护进程:
daemon=True,主进程结束则强制终止,适用于辅助任务; - 进程变量不共享:需通过 Queue/Pipe 实现通信,Queue 更安全,Pipe 更快;
- 自定义进程:继承 Process 类并重写
run(),适合封装复杂逻辑。
这些知识点覆盖了 Process 的核心用法,其中「Queue 通信」和「继承类创建进程」是实际开发中最常用的,建议优先掌握。
12.9 进程池
进程池 的作用是复用进程、减少进程创建销毁的开销,提交任务后需要获取每个任务的执行结果
Python 中常用的进程池模块有两个:
multiprocessing.Pool(基础版)concurrent.futures.ProcessPoolExecutor(更现代、API 更统一,推荐使用)
下面以 concurrent.futures.ProcessPoolExecutor 为例讲解(multiprocessing.Pool 逻辑类似,仅 API 略有差异)。
- 核心概念逐一解释
(1)
with语句:进程池的优雅管理with是 Python 的上下文管理器,用于自动管理进程池的生命周期——进入with块时创建进程池,退出时自动关闭进程池(无需手动调用shutdown()),避免资源泄漏。
from concurrent.futures import ProcessPoolExecutor
import time
def task(num):
time.sleep(1)
return num * 2
用 with 管理进程池,自动创建/关闭
with ProcessPoolExecutor(max_workers=2) as pool:
提交任务...
future = pool.submit(task, 5)
print(future.result()) 输出 10
退出 with 块后,进程池自动关闭,无需手动 pool.shutdown()
(2)map 方法:批量提交任务(简化版)
map 是进程池的“快捷方法”,类似 Python 内置的 map,批量将可迭代对象的元素作为参数传给任务函数,返回结果的顺序与任务提交顺序一致(即使某个任务先完成,也会等前面的任务结果)。
- 优点:代码简洁,适合“批量执行相同逻辑、按顺序取结果”的场景;
- 缺点:无法实时获取已完成的任务结果(必须等所有任务完成)。
from concurrent.futures import ProcessPoolExecutor
def task(num):
return num * 2
if __name__ == "__main__": 多进程必须加这个(Windows 系统要求)
with ProcessPoolExecutor(max_workers=2) as pool:
批量提交任务:[1,2,3] 依次传给 task
results = pool.map(task, [1,2,3])
遍历结果(顺序与输入一致:2,4,6)
for res in results:
print(res)
(3)submit() + done() + result():单个任务的结果判断
pool.submit(func, *args):提交单个任务到进程池,返回一个Future对象(可以理解为“未来会拿到结果的容器”);future.done():判断该任务是否执行完成(返回True/False);future.result():获取任务执行结果(如果任务未完成,会阻塞直到结果返回)。
from concurrent.futures import ProcessPoolExecutor
import time
def task(num):
time.sleep(2)
return num * 2
if __name__ == "__main__":
with ProcessPoolExecutor(max_workers=1) as pool:
future = pool.submit(task, 5)
轮询判断任务是否完成
while not future.done():
print("任务还在执行中...")
time.sleep(1)
获取结果
print("任务完成,结果:", future.result()) 输出 10
(4)as_completed():实时获取已完成的任务结果
concurrent.futures.as_completed(futures) 是一个迭代器,按任务完成的顺序返回 Future 对象((Future可以理解为“未来会拿到结果的容器”);)(不是提交顺序),适合“谁先完成就先处理谁”的场景(比如多任务爬取、批量计算)。
from concurrent.futures import ProcessPoolExecutor, as_completed
import time
def task(num):
time.sleep(num) 任务1睡1秒,任务2睡2秒,任务3睡3秒
return f"任务{num}完成,结果:{num*2}"
if __name__ == "__main__":
with ProcessPoolExecutor(max_workers=3) as pool:
提交3个任务,返回 Future 列表
futures = [pool.submit(task, i) for i in [1,2,3]]
as_completed 按完成顺序遍历(先完成的先输出)
for future in as_completed(futures):
res = future.result()
print(res)
输出顺序:任务1完成 → 任务2完成 → 任务3完成(而非提交顺序的1,2,3,但这里刚好一致,换个sleep值就会体现)
(5)callback 回调函数 + add_done_callback():任务完成自动触发
callback(回调函数)是任务执行完成后自动调用的函数,通过 future.add_done_callback(func) 绑定。
- 回调函数的参数必须是
Future对象(Future可以理解为“未来会拿到结果的容器”); - 适合“任务完成后无需主进程等待,自动处理结果”的场景(比如异步通知、结果入库)。
from concurrent.futures import ProcessPoolExecutor
def task(num):
return num * 2
定义回调函数(参数必须是 future 对象)
def callback_func(future):
res = future.result()
print(f"回调函数执行:任务结果是 {res}")
if __name__ == "__main__":
with ProcessPoolExecutor(max_workers=2) as pool:
future = pool.submit(task, 5)
绑定回调函数:任务完成后自动执行 callback_func
future.add_done_callback(callback_func)
输出:回调函数执行:任务结果是 10
(6)补充:multiprocessing.Pool 中的 callback
如果用旧版 multiprocessing.Pool,apply_async/map_async 支持直接传 callback 参数(无需 add_done_callback):
from multiprocessing import Pool
def task(num):
return num * 2
def callback_func(res):
print(f"回调结果:{res}")
if __name__ == "__main__":
with Pool(processes=2) as pool:
异步提交任务并指定回调
pool.apply_async(task, args=(5,), callback=callback_func)
pool.close() 关闭提交入口
pool.join() 等待所有任务完成
输出:回调结果:10
关键用法对比(新手必看)
| 方法/关键字 | 核心作用 | 适用场景 |
|---|---|---|
with + 进程池 | 自动管理进程池生命周期(创建/关闭) | 所有场景,优先使用,避免忘记关闭进程池 |
map | 批量提交任务,按提交顺序返回结果 | 任务数量多、需要按顺序取结果的简单场景 |
as_completed | 按任务完成顺序返回结果,实时处理 | 任务执行时间不一、需要优先处理完成任务 |
add_done_callback | 任务完成后自动触发回调函数 | 异步处理结果(无需主进程等待/轮询) |
done() | 手动判断任务是否完成 | 需要自定义轮询逻辑的场景 |
完整综合示例 下面的代码整合了所有核心用法,你可以直接运行体验:
from concurrent.futures import ProcessPoolExecutor, as_completed
import time
import random
定义耗时任务
def random_task(task_id):
随机睡眠1-3秒,模拟不同执行时间的任务
sleep_time = random.randint(1, 3)
time.sleep(sleep_time)
return f"任务{task_id}完成,耗时{sleep_time}秒"
定义回调函数
def handle_result(future):
"""回调函数:处理单个任务的结果"""
res = future.result()
print(f"【回调】{res}")
if __name__ == "__main__":
========== 1. 用 with 管理进程池 ==========
with ProcessPoolExecutor(max_workers=2) as pool:
========== 2. 批量提交任务(map 方法) ==========
print("===== map 方法(按提交顺序) =====")
map_results = pool.map(random_task, [1,2,3])
for res in map_results:
print(f"【map】{res}")
========== 3. 异步提交 + as_completed + 回调 ==========
print("\n===== as_completed + 回调(按完成顺序) =====")
提交3个任务,获取 Future 列表
futures = []
for task_id in [4,5,6]:
future = pool.submit(random_task, task_id)
绑定回调函数
future.add_done_callback(handle_result)
futures.append(future)
用 as_completed 遍历已完成的任务(手动处理结果,与回调二选一即可)
for future in as_completed(futures):
if future.done(): 手动判断任务是否完成(可选)
res = future.result()
print(f"【as_completed】{res}")
总结
with是进程池的最佳实践:自动管理进程池生命周期,避免资源泄漏,所有场景优先使用;- 获取结果的核心方式:
- 简单批量任务用
map(按提交顺序); - 实时处理完成任务用
as_completed(按完成顺序); - 异步处理结果用
callback回调(无需主进程等待);
- 简单批量任务用
- 关键方法:
done():手动判断任务状态;add_done_callback:给单个任务绑定回调;as_completed:遍历已完成的任务(返回 Future 对象)(Future可以理解为“未来会拿到结果的容器”)。
这些概念的核心目标都是高效、安全地获取进程池任务的执行结果,你可以根据“是否需要按顺序”“是否异步处理”选择对应的方法。
12.10 多线程(Thread)
你问到的这些是 Python 并发编程的核心概念(线程创建、线程池、GIL、多进程/多线程对比),我会从新手易懂的角度,用“定义+示例+场景”的方式逐一拆解,帮你建立清晰的认知。
一、先理清核心背景 Python 中的并发主要分两种:多线程(Thread)和多进程(Process),而 GIL 是影响 Python 多线程性能的关键“特殊规则”,线程池则是优化线程使用的工具。下面逐个讲解:
- 使用
Thread直接创建线程(基础方式)threading.Thread是 Python 实现线程的核心类,直接创建的方式是:将任务函数传给Thread的target参数,再调用start()启动线程。
核心特点:
- 简单直观,适合快速创建少量线程;
- 任务逻辑写在普通函数中,与线程类解耦。
完整示例:
import threading
import time
定义线程要执行的任务函数
def task(name, sleep_time):
print(f"线程 {name} 启动")
time.sleep(sleep_time) 模拟耗时操作
print(f"线程 {name} 结束")
if __name__ == "__main__":
1. 创建线程对象:target 指定任务函数,args 传参数(元组形式)
t1 = threading.Thread(target=task, args=("t1", 2))
t2 = threading.Thread(target=task, args=("t2", 1))
2. 启动线程(关键:start() 才会真正创建线程并执行,不是调用 run())
t1.start()
t2.start()
3. 等待线程结束(可选:join() 会阻塞主线程,直到子线程完成)
t1.join()
t2.join()
print("所有线程执行完毕(主线程)")
输出(注意 t2 先结束,因为 sleep 时间更短):
线程 t1 启动
线程 t2 启动
线程 t2 结束
线程 t1 结束
所有线程执行完毕(主线程)
- 继承
Thread类创建线程(自定义方式) 另一种创建线程的方式是继承threading.Thread,并重写run()方法(run()是线程的核心执行逻辑)。
核心特点:
- 适合需要给线程添加自定义属性/方法的场景;
- 任务逻辑封装在
run()中,代码结构更面向对象。
完整示例:
import threading
import time
继承 Thread 类,自定义线程类
class MyThread(threading.Thread):
初始化:可以自定义参数
def __init__(self, name, sleep_time):
必须先调用父类的初始化方法
super().__init__(name=name) name 是线程名
self.sleep_time = sleep_time
重写 run() 方法:线程启动后会自动执行这个方法
def run(self):
print(f"自定义线程 {self.name} 启动")
time.sleep(self.sleep_time)
print(f"自定义线程 {self.name} 结束")
if __name__ == "__main__":
创建自定义线程对象
t1 = MyThread("t1", 2)
t2 = MyThread("t2", 1)
启动线程(start() 会自动调用 run())
t1.start()
t2.start()
等待线程结束
t1.join()
t2.join()
print("所有自定义线程执行完毕")
输出和上例一致,核心区别是“任务逻辑写在 run() 中”。
- 线程池(
concurrent.futures.ThreadPoolExecutor) 线程池是预先创建一组线程,复用这些线程执行多个任务,避免频繁创建/销毁线程的开销(线程创建是有成本的)。
核心特点:
- 自动管理线程数量,避免创建过多线程导致系统资源耗尽;
- API 简洁,支持批量提交任务、获取结果、回调函数等;
- 推荐使用
concurrent.futures.ThreadPoolExecutor(Python 3.2+ 内置,更现代)。
完整示例(结合 with 管理生命周期):
from concurrent.futures import ThreadPoolExecutor
import time
def task(task_id, sleep_time):
print(f"任务 {task_id} 启动(线程池)")
time.sleep(sleep_time)
return f"任务 {task_id} 完成,耗时 {sleep_time} 秒"
if __name__ == "__main__":
创建线程池,指定最大线程数为 2
with ThreadPoolExecutor(max_workers=2) as pool:
方式1:提交单个任务
future1 = pool.submit(task, 1, 2)
future2 = pool.submit(task, 2, 1)
获取任务结果(result() 会阻塞直到结果返回)
print(future1.result())
print(future2.result())
方式2:批量提交任务(map 方法,按提交顺序返回结果)
print("\n===== 批量任务 =====")
results = pool.map(task, [3,4,5], [1,2,1])
for res in results:
print(res)
输出:
任务 1 启动(线程池)
任务 2 启动(线程池)
任务 2 完成,耗时 1 秒
任务 1 完成,耗时 2 秒
===== 批量任务 =====
任务 3 启动(线程池)
任务 4 启动(线程池)
任务 3 完成,耗时 1 秒
任务 5 启动(线程池)
任务 4 完成,耗时 2 秒
任务 5 完成,耗时 1 秒
关键优势: 线程池会复用 2 个线程执行 5 个任务,而非创建 5 个线程,大幅降低资源开销。
- GIL 锁(Python 全局解释器锁)
GIL 是 CPython 解释器(Python 官方实现)的一个核心限制,全称
Global Interpreter Lock,可以理解为:
GIL 强制同一时刻,只有一个线程能执行 Python 字节码——哪怕你的电脑是多核 CPU,Python 多线程也无法利用多核并行执行 CPU 密集型任务。
核心要点(新手必懂):
- GIL 只影响 CPython:Jython、IronPython 等其他 Python 解释器无 GIL;
- GIL 对不同任务的影响:
- ✅ IO 密集型任务(如网络请求、文件读写、等待数据库响应):GIL 影响很小,多线程依然高效(因为线程等待 IO 时会释放 GIL,其他线程可以执行);
- ❌ CPU 密集型任务(如数学计算、循环处理):GIL 导致多线程无法并行,只能“并发”(切换执行),效率甚至不如单线程;
- 解决 GIL 限制的方案:用多进程(每个进程有独立的 Python 解释器和 GIL,可利用多核)。
直观对比(CPU 密集型任务):
CPU 密集型任务:计算 1 到 n 的和
def cpu_task(n):
total = 0
for i in range(n):
total += i
return total
多线程执行:受 GIL 限制,无法利用多核
多进程执行:无 GIL 限制,可利用多核,速度更快
- 多进程 VS 多线程(核心对比)
| 维度 | 多线程(Thread) | 多进程(Process) |
|---|---|---|
| GIL 影响 | 受 GIL 限制,CPU 密集型任务无法并行 | 不受 GIL 限制,可利用多核并行执行 |
| 资源开销 | 开销小(线程是轻量级,共享进程内存) | 开销大(进程是独立的,有自己的内存空间) |
| 数据共享 | 易(共享进程内存,需加锁如 Lock 避免冲突) | 难(进程间内存隔离,需用 Queue/Pipe 通信) |
| 创建 / 销毁速度 | 快 | 慢 |
| 适用场景 | IO 密集型任务(爬虫、接口请求、文件读写) | CPU 密集型任务(数学计算、数据处理) |
场景举例:
- 写爬虫爬取 100 个网页:用多线程(大部分时间在等待网络响应,IO 密集);
- 批量处理 100 个大文件的数据分析:用多进程(大部分时间在计算,CPU 密集);
- 既要有 IO 又要有计算:可结合(如多进程 + 进程内多线程)。
多进程简单示例(对比线程):
from multiprocessing import Process
import time
def task(name, sleep_time):
print(f"进程 {name} 启动")
time.sleep(sleep_time)
print(f"进程 {name} 结束")
if __name__ == "__main__":
p1 = Process(target=task, args=("p1", 2))
p2 = Process(target=task, args=("p2", 1))
p1.start()
p2.start()
p1.join()
p2.join()
print("所有进程执行完毕")
总结
- 线程创建方式:
- 直接用
threading.Thread(target=函数):简单,适合通用场景; - 继承
Thread重写run():面向对象,适合自定义线程逻辑;
- 直接用
- 线程池:复用线程,降低创建/销毁开销,推荐用
ThreadPoolExecutor+with管理; - GIL 锁:CPython 的“特殊规则”,导致多线程无法并行执行 CPU 密集型任务,IO 密集型任务不受影响;
- 多进程 VS 多线程:
- IO 密集选多线程(开销小),CPU 密集选多进程(利用多核);
- 多线程共享内存易数据冲突,多进程内存隔离需专门通信。
这些概念的核心是:根据任务类型(IO/CPU 密集)选择合适的并发方式,避开 GIL 的限制,最大化程序效率。
12.11 协程
协程(Coroutine)是 Python 3.5+ 引入的轻量级“异步任务”,本质是可以暂停/恢复执行的函数,相比线程/进程,协程的开销极低(创建百万级协程也不会耗尽资源),核心用于处理 IO 密集型任务(如网络请求、文件读写)。
二、核心概念逐一拆解
- 协程函数(Coroutine Function)
定义:用 async def 关键字定义的函数,就是协程函数(区别于普通的 def 函数)。
特点:调用协程函数不会立即执行,而是返回一个“协程对象”。
这是一个协程函数(async def 标识)
async def coro_func():
print("协程函数执行")
return "执行完成"
调用协程函数 → 不会执行函数体,只会返回协程对象
coro_obj = coro_func()
print(type(coro_obj)) 输出:<class 'coroutine'>
- 协程对象(Coroutine Object)
定义:调用协程函数后返回的对象(如上面的
coro_obj),是协程的“可执行载体”。 特点:
- 协程对象本身不能直接执行,必须通过以下方式触发:
- 用
await关键字等待(只能在协程函数/异步上下文里用); - 交给事件循环(
asyncio.run()/asyncio.create_task())执行。
- 用
await关键字 核心作用:暂停当前协程的执行,等待“可等待对象”(如其他协程、asyncio.Task、aiohttp异步请求等)完成,完成后再恢复当前协程。 关键规则:
await只能用在async def定义的协程函数内部;await后面必须跟可等待对象(不能是普通函数/对象);- 等待期间,事件循环可以去执行其他协程,实现“并发”。
import asyncio
async def wait_task():
print("开始等待 2 秒")
await 等待 asyncio.sleep(模拟IO等待,非CPU阻塞)
await asyncio.sleep(2) 这里会暂停当前协程,事件循环可执行其他任务
print("等待结束")
async def main():
await 触发协程对象执行
await wait_task()
启动事件循环,执行主协程
asyncio.run(main())
- 多个任务的同步/异步执行
- 同步执行:任务按顺序执行,一个任务完成后才开始下一个(总耗时=所有任务耗时之和);
- 异步执行:多个任务“并发”执行,一个任务等待 IO 时,切换执行另一个任务(总耗时≈最慢任务的耗时)。
核心区别:同步是“排队做”,异步是“能同时做的就同时做(IO等待时切换)”。
三、实战:传统同步 VS 协程异步下载图片 先明确前提:下载图片是典型的 IO 密集型任务(大部分时间在等待网络响应),协程能极大提升效率。
准备工作
先安装依赖(异步网络请求库 aiohttp):
pip install requests aiohttp requests:同步请求,aiohttp:异步请求
示例1:传统同步方式下载图片
import requests
import time
要下载的图片链接(3张图片,模拟不同下载耗时)
IMG_URLS = [
"https://picsum.photos/200/200?random=1",
"https://picsum.photos/200/200?random=2",
"https://picsum.photos/200/200?random=3",
]
def download_img_sync(url, idx):
"""同步下载单张图片"""
print(f"开始下载第 {idx+1} 张图片")
同步请求(阻塞:直到下载完成才会执行下一步)
response = requests.get(url)
保存图片
with open(f"sync_img_{idx+1}.jpg", "wb") as f:
f.write(response.content)
print(f"第 {idx+1} 张图片下载完成")
def main_sync():
"""同步下载所有图片"""
start_time = time.time()
按顺序下载:下载完第1张,才会下载第2张
for idx, url in enumerate(IMG_URLS):
download_img_sync(url, idx)
end_time = time.time()
print(f"同步下载总耗时:{end_time - start_time:.2f} 秒")
if __name__ == "__main__":
main_sync()
输出(示例):
开始下载第 1 张图片
第 1 张图片下载完成
开始下载第 2 张图片
第 2 张图片下载完成
开始下载第 3 张图片
第 3 张图片下载完成
同步下载总耗时:3.25 秒
核心问题:同步下载时,每个 requests.get() 都会阻塞主线程,等待网络响应的时间里,程序完全闲置,总耗时是所有任务耗时之和。
示例2:协程异步方式下载图片
import asyncio
import aiohttp
import time
IMG_URLS = [
"https://picsum.photos/200/200?random=1",
"https://picsum.photos/200/200?random=2",
"https://picsum.photos/200/200?random=3",
]
async def download_img_async(session, url, idx):
"""异步下载单张图片(协程函数)"""
print(f"开始下载第 {idx+1} 张图片")
await 等待异步请求:请求期间,事件循环去执行其他协程
async with session.get(url) as response:
异步读取响应内容(await 不能少)
img_data = await response.read()
异步写入文件(可选:用 aiofiles,这里简化用同步写入)
with open(f"async_img_{idx+1}.jpg", "wb") as f:
f.write(img_data)
print(f"第 {idx+1} 张图片下载完成")
async def main_async():
"""异步下载所有图片"""
start_time = time.time()
创建异步 HTTP 会话(复用连接,提升效率)
async with aiohttp.ClientSession() as session:
方式1:创建任务列表,并发执行(推荐)
tasks = []
for idx, url in enumerate(IMG_URLS):
创建异步任务(不会立即执行,需 await)
task = asyncio.create_task(download_img_async(session, url, idx))
tasks.append(task)
等待所有任务完成(并发执行)
await asyncio.gather(*tasks)
end_time = time.time()
print(f"异步下载总耗时:{end_time - start_time:.2f} 秒")
if __name__ == "__main__":
启动事件循环,执行主协程
asyncio.run(main_async())
输出(示例):
开始下载第 1 张图片
开始下载第 2 张图片
开始下载第 3 张图片
第 2 张图片下载完成
第 1 张图片下载完成
第 3 张图片下载完成
异步下载总耗时:1.12 秒
核心优势:
- 所有图片几乎同时开始下载,
await session.get()等待网络响应时,事件循环会切换执行其他协程; - 总耗时≈最慢的单个任务耗时,而非总和,效率大幅提升。
补充:多个任务的“同步/异步”执行对比
import asyncio
import time
模拟耗时任务(IO 等待)
async def task(name, sleep_time):
print(f"任务 {name} 开始")
await asyncio.sleep(sleep_time) 模拟 IO 等待(非阻塞)
print(f"任务 {name} 结束")
return f"任务 {name} 结果"
1. 同步执行(按顺序)
async def sync_run():
start = time.time()
逐个 await:执行完 task1 才执行 task2
res1 = await task("t1", 2)
res2 = await task("t2", 1)
print(f"同步结果:{res1}, {res2}")
print(f"同步耗时:{time.time()-start:.2f} 秒") ≈3 秒
2. 异步执行(并发)
async def async_run():
start = time.time()
创建任务,并发执行
task1 = asyncio.create_task(task("t1", 2))
task2 = asyncio.create_task(task("t2", 1))
等待所有任务完成
res1 = await task1
res2 = await task2
print(f"异步结果:{res1}, {res2}")
print(f"异步耗时:{time.time()-start:.2f} 秒") ≈2 秒
if __name__ == "__main__":
print("===== 同步执行 =====")
asyncio.run(sync_run())
print("\n===== 异步执行 =====")
asyncio.run(async_run())
核心概念总结(新手必记)
| 概念 | 核心定义 |
|---|---|
| 协程 | 可暂停/恢复执行的轻量级异步任务,由程序员控制执行时机,开销远低于线程/进程 |
| 协程函数 | 用 async def 定义的函数,调用后返回协程对象,而非立即执行 |
| 协程对象 | 协程函数的返回值,是协程的“可执行载体”,需通过 await/事件循环触发执行 |
await | 暂停当前协程,等待“可等待对象”完成,期间事件循环可执行其他协程 |
| 同步执行 | 任务按顺序执行,一个完成才开始下一个,总耗时=所有任务耗时之和 |
| 异步执行 | 任务并发执行,IO 等待时切换执行其他任务,总耗时≈最慢任务耗时 |
实战要点总结
- 协程的核心价值:解决 IO 密集型任务的效率问题(如网络请求、下载),避免等待期间程序闲置;
- 异步下载图片的关键:
- 用
aiohttp替代requests(异步网络请求); - 用
asyncio.create_task()创建并发任务,asyncio.gather()等待所有任务完成; - 所有 IO 操作(请求、读数据)都要加
await;
- 用
- 协程的执行依赖事件循环(
asyncio.run()是启动事件循环的快捷方式)。
简单来说:协程是 Python 异步编程的核心,通过 async/await 让程序在 IO 等待时“不闲着”,用极低的开销实现高并发,尤其适合爬虫、接口调用、文件读写等场景。
13. 性能优化
13.1 代码优化原则
- 选择合适的数据结构:根据具体需求选择合适的数据结构,如列表、字典、集合等。
- 避免不必要的循环:尽量减少循环次数,使用更高效的算法。
- 使用生成器:生成器可以节省内存,提高性能。
- 避免重复计算:将重复计算的结果缓存起来,如使用
lru_cache装饰器。 - 使用内置函数:Python的内置函数通常是用C实现的,比Python代码快很多。
- 避免全局变量:局部变量的访问速度比全局变量快。
- 使用列表推导式:列表推导式比普通的for循环快。
13.2 性能分析工具
- time模块:用于测量代码的执行时间。
- cProfile模块:用于分析代码的性能瓶颈。
- timeit模块:用于精确测量小代码片段的执行时间。
# 使用time模块测量执行时间
import time
start_time = time.time()
# 执行代码
result = sum([i for i in range(1000000)])
end_time = time.time()
execution_time = end_time - start_time
print(f"Execution time: {execution_time} seconds")
# 使用timeit模块测量执行时间
import timeit
execution_time = timeit.timeit("sum([i for i in range(1000000)])", number=1)
print(f"Execution time: {execution_time} seconds")
# 使用cProfile模块分析性能
import cProfile
def func():
result = sum([i for i in range(1000000)])
return result
cProfile.run("func()")
13.3 常用优化技巧
13.3.1 使用生成器替代列表
# 使用列表
numbers_list = [i for i in range(1000000)]
sum_list = sum(numbers_list)
# 使用生成器
numbers_gen = (i for i in range(1000000))
sum_gen = sum(numbers_gen)
13.3.2 使用内置函数
# 使用普通的for循环
total = 0
for i in range(1000000):
total += i
# 使用内置函数sum
total = sum(range(1000000))
13.3.3 使用lru_cache装饰器
from functools import lru_cache
# 不使用缓存
import time
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
start_time = time.time()
result = fibonacci(40)
end_time = time.time()
print(f"Result: {result}")
print(f"Execution time: {end_time - start_time} seconds")
# 使用缓存
@lru_cache(maxsize=None)
def fibonacci_cached(n):
if n <= 1:
return n
return fibonacci_cached(n - 1) + fibonacci_cached(n - 2)
start_time = time.time()
result = fibonacci_cached(40)
end_time = time.time()
print(f"Result: {result}")
print(f"Execution time: {end_time - start_time} seconds")
14. 调试与测试
14.1 调试技巧
14.1.1 使用print语句
最简单的调试方法是使用print语句输出变量的值。
def add(a, b):
print(f"a = {a}, b = {b}")
return a + b
result = add(3, 5)
print(f"result = {result}")
14.1.2 使用pdb调试器
pdb是Python的内置调试器,可以用于单步执行代码、查看变量的值等。
import pdb
def add(a, b):
pdb.set_trace() # 设置断点
return a + b
result = add(3, 5)
print(f"result = {result}")
14.1.3 使用IDE的调试功能
大多数集成开发环境(如PyCharm、VS Code等)都提供了强大的调试功能,可以更方便地调试代码。
14.2 测试方法
14.2.1 单元测试
单元测试用于测试代码中的单个函数或类。Python的unittest模块提供了单元测试的功能。
import unittest
def add(a, b):
"""计算两个数的和"""
return a + b
class TestAdd(unittest.TestCase):
"""测试add函数"""
def test_add_positive_numbers(self):
"""测试两个正数相加"""
self.assertEqual(add(3, 5), 8)
def test_add_negative_numbers(self):
"""测试两个负数相加"""
self.assertEqual(add(-3, -5), -8)
def test_add_zero(self):
"""测试加零"""
self.assertEqual(add(3, 0), 3)
self.assertEqual(add(0, 5), 5)
if __name__ == "__main__":
unittest.main()
14.2.2 测试用例设计
设计测试用例时,应该考虑以下几点:
- 边界条件:如空列表、零、最大值、最小值等。
- 正常情况:如正常的输入和输出。
- 异常情况:如错误的输入、除以零等。
14.2.3 使用pytest
pytest是一个更简单、更强大的测试框架,可以替代unittest模块。
# 使用pytest测试
def add(a, b):
"""计算两个数的和"""
return a + b
def test_add_positive_numbers():
"""测试两个正数相加"""
assert add(3, 5) == 8
def test_add_negative_numbers():
"""测试两个负数相加"""
assert add(-3, -5) == -8
def test_add_zero():
"""测试加零"""
assert add(3, 0) == 3
assert add(0, 5) == 5
15. 学习资源推荐
15.1 官方文档
- Python官方文档:docs.python.org/3/
- Python标准库文档:docs.python.org/3/library/
- Python教程:docs.python.org/3/tutorial/
15.2 在线教程
- Codecademy Python课程:www.codecademy.com/learn/learn…
- Coursera Python for Everybody:www.coursera.org/specializat…
- edX Introduction to Python:www.edx.org/course/intr…
- Real Python:realpython.com/
- W3Schools Python教程:www.w3schools.com/python/
15.3 书籍推荐
- 《Python编程:从入门到实践》:Eric Matthes著,适合初学者。
- 《流畅的Python》:Luciano Ramalho著,适合有一定Python基础的读者。
- 《Python Cookbook》:David Beazley和Brian K. Jones著,提供各种Python编程技巧和解决方案。
- 《Effective Python》:Brett Slatkin著,提供59个Python编程的最佳实践。
- 《Python核心编程》:Wesley Chun著,全面介绍Python的核心特性。
15.4 社区与论坛
- Stack Overflow:stackoverflow.com/questions/t…
- Python官方论坛:discuss.python.org/
- Reddit Python:www.reddit.com/r/Python/
- Python中文社区:www.python.org.cn/
15.5 练习网站
- LeetCode:leetcode.com/(算法练习)
- HackerRank:www.hackerrank.com/(编程挑战)
- Codewars:www.codewars.com/(编程练习)
- Project Euler:projecteuler.net/(数学和计算机科学问题…
总结
Python是一种简单易学、功能强大的编程语言,广泛应用于Web开发、数据科学、人工智能、自动化脚本等领域。本教程介绍了Python的基本概念、语法、数据类型、控制流、函数、面向对象编程、模块与包、文件操作、异常处理、常用标准库、高级特性、性能优化、调试与测试等内容,希望能够帮助读者快速掌握Python编程。
学习Python的关键是多练习、多实践,通过编写实际项目来提高编程能力。祝大家学习愉快!