Python 进阶核心知识汇总:闭包、装饰器、生成器、并发、正则与客户管理实战案例
今天内容较多,但不必一次全记住。建议分 4 个时段学习:
| 时段 | 内容 | 建议时长 |
|---|---|---|
| 上午 | 闭包、装饰器、迭代器、生成器、拷贝 | 1.5h |
| 下午 | 模块与包、正则表达式 | 1.5h |
| 傍晚 | 进程与线程、网络编程基础 | 1.5h |
| 晚上 | 客户信息管理系统案例 + 总复习 | 1h |
小白提示:今天很多是「了解 + 会用」级别,不必每个细节都背下来。先理解思想,以后写项目时会反复用到。
一、闭包(Closure)
1.1 什么是闭包?
闭包 = 内层函数引用了外层函数的变量,并且外层函数已经执行完毕,但内层函数仍然"记住"那些变量。
类比:外层函数是一个「工厂」,内层函数是工厂生产的「产品」,产品里带着工厂的配置参数。
def outer(x):
def inner(y):
return x + y # inner 记住了 outer 的参数 x
return inner
add5 = outer(5) # 得到一个「加 5」的函数
print(add5(3)) # 8
print(add5(10)) # 15
1.2 闭包有什么用?
- 保存状态(不用全局变量)
- 实现装饰器(下一节会学)
def make_counter(start=0):
count = start
def counter():
nonlocal count
count += 1
return count
return counter
c = make_counter(10)
print(c()) # 11
print(c()) # 12
二、装饰器(Decorator)
2.1 为什么需要装饰器?
场景:你有 10 个函数,都想在运行前后打印日志。如果每个函数里都写 print("开始")……代码重复。
装饰器可以在不修改原函数代码的情况下,动态增加功能。
2.2 用闭包实现装饰器
def timer(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} 耗时 {time.time()-start:.4f} 秒")
return result
return wrapper
def slow_func():
import time
time.sleep(1)
return "完成"
# 手动装饰
slow_func = timer(slow_func)
slow_func()
2.3 @ 语法糖(常用写法)
@timer
def slow_func():
import time
time.sleep(1)
return "完成"
slow_func() # 自动被 timer 包装
@timer 等价于 slow_func = timer(slow_func)
2.4 带参数的装饰器
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}")
greet("小明") # 打印 3 次
2.5 多层装饰器
规则:离函数最近的装饰器先包装(最内层)。
@deco1
@deco2
def hello():
print("hello")
# 等价于 hello = deco1(deco2(hello))
# 执行顺序:deco1 前 → deco2 前 → hello → deco2 后 → deco1 后
2.6 保留函数信息(functools.wraps)
from functools import wraps
def my_decorator(func):
@wraps(func) # 保留原函数的 __name__、__doc__ 等
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
三、迭代器(Iterator)
3.1 可迭代对象 vs 迭代器
| 概念 | 说明 | 例子 |
|---|---|---|
| 可迭代对象 | 实现了 __iter__() |
list、str、dict、tuple |
| 迭代器 | 实现了 __iter__() 和 __next__() |
由 iter() 返回 |
lst = [1, 2, 3]
it = iter(lst) # 获取迭代器
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# next(it) # StopIteration(没有更多元素)
3.2 自定义迭代器
class Counter:
def __init__(self, max_count):
self.max_count = max_count
self.count = 0
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_count:
raise StopIteration
self.count += 1
return self.count
for num in Counter(5):
print(num) # 1 2 3 4 5
小白理解:for 循环背后就是在调用 iter() 和 next()。
四、生成器(Generator)
4.1 什么是生成器?
生成器是一种特殊的迭代器,用 yield 关键字暂停函数执行并返回值,下次从暂停处继续。
好处:按需生成数据,节省内存(不用一次性创建整个列表)。
4.2 用 yield 创建生成器
def my_range(n):
for i in range(n):
yield i # 暂停,返回 i
gen = my_range(5)
print(next(gen)) # 0
for x in gen:
print(x) # 1 2 3 4
4.3 生成器表达式
# 列表推导式(一次性生成全部)
squares_list = [x**2 for x in range(1000000)]
# 生成器表达式(按需生成,省内存)
squares_gen = (x**2 for x in range(1000000))
4.4 send() 方法(了解)
def accumulator():
total = 0
while True:
value = yield total
if value is not None:
total += value
acc = accumulator()
next(acc) # 启动
print(acc.send(10)) # 10
print(acc.send(20)) # 30
五、浅拷贝与深拷贝
5.1 赋值 vs 拷贝
a = [1, 2, [3, 4]]
b = a # 赋值:b 和 a 指向同一个对象
b.append(5)
print(a) # [1, 2, [3, 4], 5] a 也变了!
5.2 浅拷贝
外层是新对象,内层仍共享。
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a) # 或 a.copy()(列表的方法)
b.append(5)
print(a) # [1, 2, [3, 4]] a 不变
b[2].append(99)
print(a) # [1, 2, [3, 4, 99]] 内层共享,a 变了!
5.3 深拷贝
完全独立,递归复制所有层级。
import copy
a = [1, 2, [3, 4]]
c = copy.deepcopy(a)
c[2].append(99)
print(a) # [1, 2, [3, 4]] 不受影响
| 方式 | 方法 | 适用 |
|---|---|---|
| 赋值 | b = a |
共享同一对象 |
| 浅拷贝 | copy.copy(a) |
一层嵌套 |
| 深拷贝 | copy.deepcopy(a) |
多层嵌套 |
六、命名空间与作用域(LEGB)
6.1 命名空间
命名空间 = 名字到对象的映射(像「姓名 → 人」的通讯录)。
| 类型 | 生命周期 | 例子 |
|---|---|---|
| 内置 | Python 启动到结束 | print, len |
| 全局 | 模块加载到卸载 | 模块顶层变量 |
| 局部 | 函数调用期间 | 函数内变量 |
6.2 LEGB 查找规则
Python 找变量名时,按顺序查找:
L → Local(局部)
E → Enclosing(嵌套外层)
G → Global(全局)
B → Built-in(内置)
x = "global"
def outer():
x = "enclosing"
def inner():
nonlocal x
x = "modified"
inner()
print(x) # modified
outer()
七、模块与包
7.1 什么是模块?
模块(Module) = 一个 .py 文件,里面可以定义函数、类、变量。
好处:代码分文件管理,别的程序可以 import 复用。
7.2 导入模块
# 方式1:导入整个模块
import math
print(math.sqrt(16)) # 4.0
# 方式2:导入特定成员
from math import sqrt, pi
print(sqrt(16))
# 方式3:别名
import numpy as np
# 方式4:导入全部(不推荐,污染命名空间)
from math import *
7.3 name 与程序入口
# my_utils.py
def add(a, b):
return a + b
if __name__ == "__main__":
# 只有「直接运行这个文件」时才执行
# 被 import 时不执行
print(add(1, 2))
小白理解:
- 直接运行
python my_utils.py→__name__是"__main__" - 被其他文件
import my_utils→__name__是"my_utils"
7.4 什么是包?
包(Package) = 包含 __init__.py 的文件夹,里面可以有多个模块。
mypackage/
__init__.py
module_a.py
module_b.py
subpackage/
__init__.py
module_c.py
from mypackage.module_a import func
from mypackage.subpackage.module_c import MyClass
7.5 dir() 函数
查看对象有哪些属性和方法:
import math
print(dir(math))
7.6 常用标准库(了解)
| 模块 | 用途 |
|---|---|
os |
文件和目录操作 |
sys |
系统相关 |
datetime |
日期时间 |
json |
JSON 读写 |
random |
随机数 |
re |
正则表达式 |
7.7 第三方库与 pip
pip install requests
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple requests # 国内镜像
八、正则表达式
8.1 什么是正则?
正则表达式(Regular Expression) = 描述字符串模式的规则,用于匹配、查找、替换文本。
类比:正则像「模糊搜索的关键词模板」——比如「找所有手机号」「找所有邮箱」。
8.2 常用元字符
| 元字符 | 含义 | 例子 |
|---|---|---|
. |
任意一个字符(除换行) | a.c 匹配 abc、aec |
\d |
数字 | \d+ 匹配 123 |
\w |
字母数字下划线 | |
\s |
空白字符 | |
* |
前面字符 0 次或多次 | ab* 匹配 a、ab、abb |
+ |
前面字符 1 次或多次 | |
? |
0 次或 1 次 | |
{n,m} |
n 到 m 次 | \d{11} 匹配 11 位数字 |
^ |
字符串开头 | |
$ |
字符串结尾 |
8.3 原始字符串 r""
正则里 \d 很多,用 raw string 避免 \ 被转义:
pattern = r"\d+" # 推荐
pattern = "\\d+" # 也可以,但麻烦
8.4 re 模块常用方法
import re
text = "我的电话13812345678,邮箱user@example.com"
# search:找第一个匹配
m = re.search(r"1\d{10}", text)
print(m.group()) # 13812345678
# findall:找所有匹配
phones = re.findall(r"1\d{10}", text)
# match:从字符串开头匹配
re.match(r"\d+", "123abc") # 匹配
# sub:替换
re.sub(r"\d", "*", "a1b2") # a*b*
# split:按模式分割
re.split(r"[,;]", "a,b;c") # ['a', 'b', 'c']
8.5 分组
用 () 捕获匹配的部分:
text = "2024-07-13"
m = re.match(r"(\d{4})-(\d{2})-(\d{2})", text)
print(m.group(1)) # 2024
print(m.groups()) # ('2024', '07', '13')
九、进程与线程
9.1 进程 vs 线程
| 特性 | 进程 | 线程 |
|---|---|---|
| 是什么 | 资源分配的基本单位 | CPU 调度的基本单位 |
| 内存 | 各自独立 | 共享同一进程内存 |
| 开销 | 大 | 小 |
| 崩溃 | 不影响其他进程 | 可能影响整个进程 |
类比:
- 进程 = 一家工厂(独立厂房、设备)
- 线程 = 工厂里的工人(共享厂房,协作干活)
9.2 多线程示例
import threading
import time
def task(name):
for i in range(3):
print(f"{name}: {i}")
time.sleep(0.5)
t1 = threading.Thread(target=task, args=("窗口A",))
t2 = threading.Thread(target=task, args=("窗口B",))
t1.start()
t2.start()
t1.join() # 等待 t1 结束
t2.join()
print("全部完成")
9.3 GIL 全局解释器锁(了解)
CPython 中,同一时刻只有一个线程在执行 Python 字节码。
| 任务类型 | 多线程效果 |
|---|---|
| CPU 密集型(大量计算) | 几乎无加速 |
| I/O 密集型(等待网络/磁盘) | 有效,I/O 等待时会释放 GIL |
小白建议:
- 爬虫、下载、读写文件 → 多线程 OK
- 大量数学计算 → 考虑
multiprocessing多进程
9.4 互斥锁(线程安全)
多个线程同时改同一数据可能出错,用锁保护:
import threading
count = 0
lock = threading.Lock()
def increment():
global count
for _ in range(100000):
with lock:
count += 1
threads = [threading.Thread(target=increment) for _ in range(5)]
for t in threads: t.start()
for t in threads: t.join()
print(count) # 500000
十、网络编程基础
10.1 网络是什么?
网络 = 多台计算机互相连接,交换数据。
10.2 IP 与端口
| 概念 | 说明 | 例子 |
|---|---|---|
| IP 地址 | 标识网络中的一台计算机 | 192.168.1.1 |
| 端口 | 标识计算机上的具体服务 | 80(HTTP)、443(HTTPS) |
类比:IP 像小区地址,端口像门牌号。
10.3 TCP 与 UDP
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接(先握手) | 无连接 |
| 可靠性 | 可靠,不丢包 | 不保证 |
| 速度 | 较慢 | 较快 |
| 场景 | 网页、文件传输 | 视频直播、DNS |
10.4 HTTP 基础
访问网页用的协议。
请求示例:
GET /index.html HTTP/1.1
Host: www.example.com
响应示例:
HTTP/1.1 200 OK
Content-Type: text/html
<html>...</html>
常见状态码:
| 状态码 | 含义 |
|---|---|
| 200 | 成功 |
| 404 | 页面不存在 |
| 500 | 服务器内部错误 |
10.5 用 requests 发 HTTP 请求(推荐)
import requests
response = requests.get("https://httpbin.org/get")
print(response.status_code) # 200
print(response.json())
10.6 Socket 套接字(了解)
网络通信的底层接口:
import socket
# 简单 TCP 客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("www.example.com", 80))
client.send(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n")
data = client.recv(4096)
print(data.decode()[:200])
client.close()
小白建议:日常开发用 requests 即可,Socket 了解概念即可。
十一、综合案例:客户信息管理系统
11.1 需求说明
实现一个命令行版客户管理系统,支持:
| 编号 | 功能 |
|---|---|
| 1 | 添加客户(id、姓名、电话) |
| 2 | 删除客户(按 id) |
| 3 | 修改客户(按 id) |
| 4 | 查询客户(按 id 或姓名) |
| 5 | 显示所有客户 |
| 6 | 退出 |
11.2 类设计
Customer(客户类)
- cid, name, phone
- __str__()
CustomerManager(管理类)
- customers: dict
- add / delete / update / search / show_all
11.3 完整代码
class Customer:
def __init__(self, cid, name, phone):
self.cid = cid
self.name = name
self.phone = phone
def __str__(self):
return f"ID:{self.cid} 姓名:{self.name} 电话:{self.phone}"
class CustomerManager:
def __init__(self):
self.customers = {} # {cid: Customer}
def add(self, customer):
if customer.cid in self.customers:
print(f"ID {customer.cid} 已存在,添加失败")
return False
self.customers[customer.cid] = customer
print("添加成功")
return True
def delete(self, cid):
if cid in self.customers:
del self.customers[cid]
print("删除成功")
return True
print("客户不存在")
return False
def update(self, cid, name=None, phone=None):
if cid not in self.customers:
print("客户不存在")
return False
c = self.customers[cid]
if name: c.name = name
if phone: c.phone = phone
print("修改成功")
return True
def search(self, keyword):
results = []
for c in self.customers.values():
if keyword in str(c.cid) or keyword in c.name:
results.append(c)
return results
def show_all(self):
if not self.customers:
print("暂无客户")
return
for c in self.customers.values():
print(c)
def main():
mgr = CustomerManager()
while True:
print("\n===== 客户信息管理系统 =====")
print("1.添加 2.删除 3.修改 4.查询 5.显示全部 6.退出")
choice = input("请选择:").strip()
if choice == "1":
cid = input("客户ID:").strip()
name = input("姓名:").strip()
phone = input("电话:").strip()
mgr.add(Customer(cid, name, phone))
elif choice == "2":
cid = input("要删除的客户ID:").strip()
mgr.delete(cid)
elif choice == "3":
cid = input("要修改的客户ID:").strip()
name = input("新姓名(回车跳过):").strip()
phone = input("新电话(回车跳过):").strip()
mgr.update(cid, name or None, phone or None)
elif choice == "4":
keyword = input("输入ID或姓名:").strip()
results = mgr.search(keyword)
if results:
for c in results:
print(c)
else:
print("未找到")
elif choice == "5":
mgr.show_all()
elif choice == "6":
print("再见!")
break
else:
print("无效选择,请重试")
if __name__ == "__main__":
main()
这个案例综合运用:类与对象、字典、输入输出、循环、分支、字符串——是你十天学习的「毕业作品」!
十二、十天知识总复习
Day 1 概述 + 注释 + 变量
Day 2 数据类型 + 进制 + 编码规范
Day 3 运算符 + 输入输出 + 分支 if
Day 4 循环 for/while + 字符串
Day 5 序列 + 列表 List
Day 6 元组 + 字典 + 集合
Day 7 函数
Day 8 文件操作 + 异常处理
Day 9 面向对象(封装、继承、多态)
Day 10 高级语法 + 模块 + 正则 + 并发 + 网络 + 综合案例
后续学习路线建议
| 方向 | 推荐内容 |
|---|---|
| Web 开发 | Flask / Django |
| 数据分析 | Pandas、NumPy、Matplotlib |
| 人工智能 | PyTorch、机器学习基础 |
| 自动化 | 爬虫、办公自动化(openpyxl) |
十三、重点易错点
| 易错点 | 正确理解 |
|---|---|
| 装饰器改变了函数名 | 用 @wraps(func) 保留 |
| 浅拷贝 vs 深拷贝 | 有嵌套列表用 deepcopy |
| GIL | CPU 密集用多进程,I/O 密集用多线程 |
正则 \d |
用 r"\d+" 原始字符串 |
if __name__ == "__main__" |
直接运行才执行,import 不执行 |
十四、练习题
基础题
1. 编写装饰器 @log,在函数调用前后打印「开始执行 xxx」和「执行完毕 xxx」。
2. 编写生成器 fibonacci(),依次产生斐波那契数:0, 1, 1, 2, 3, 5, 8……
3. 用正则验证手机号:1 开头,共 11 位数字。
4. 创建模块 calc.py(含 add、sub 函数),在另一个文件中 import 并使用。
5. 完善客户管理系统:添加「保存到 JSON 文件」和「从 JSON 加载」功能。
进阶题
6. 用多线程模拟 3 个窗口卖 10 张票,用锁保证不会超卖。
7. 用 requests 访问 https://httpbin.org/get,打印状态码和响应 JSON。
8. 用正则从 "<a href='https://example.com'>点击这里</a>" 中提取 URL 和链接文字。
参考答案
点击展开参考答案
import re, json, threading, requests
from functools import wraps
# 1. log 装饰器
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"开始执行 {func.__name__}")
result = func(*args, **kwargs)
print(f"执行完毕 {func.__name__}")
return result
return wrapper
# 2. 斐波那契生成器
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 3. 手机号验证
def is_phone(s):
return bool(re.fullmatch(r"1\d{10}", s))
# 5. JSON 持久化(在 CustomerManager 中添加)
class CustomerManager:
def save(self, path="customers.json"):
data = {cid: {"cid": c.cid, "name": c.name, "phone": c.phone}
for cid, c in self.customers.items()}
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
def load(self, path="customers.json"):
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
for info in data.values():
c = Customer(info["cid"], info["name"], info["phone"])
self.customers[c.cid] = c
except FileNotFoundError:
pass
# 6. 卖票
tickets = 10
lock = threading.Lock()
def sell(name):
global tickets
while True:
with lock:
if tickets <= 0:
break
tickets -= 1
print(f"{name} 卖出1张,剩余 {tickets}")
for name in ["窗口A", "窗口B", "窗口C"]:
threading.Thread(target=sell, args=(name,)).start()
# 7. HTTP 请求
resp = requests.get("https://httpbin.org/get")
print(resp.status_code, resp.json())
# 8. 提取 HTML 链接
html = "<a href='https://example.com'>点击这里</a>"
m = re.search(r"<a href='(.+?)'>(.+?)</a>", html)
url, text = m.groups()
更多推荐
所有评论(0)