1. 为什么Python开发者必须掌握并发编程?
在当今互联网应用中,处理高并发请求已成为标配能力。我曾在电商秒杀项目中深刻体会到:当单线程程序遇到5000+QPS时,响应时间直接从200ms飙升到15秒以上。这就是为什么我们需要并发编程——它能让你的Python程序像快餐店开多个收银台一样,同时服务更多顾客。
Python通过三种主流方式实现并发:
- 多线程:适合I/O密集型任务,比如爬虫请求网页时,一个线程等待响应时其他线程可以继续工作
- 多进程:绕过GIL限制,真正利用多核CPU,适合计算密集型任务
- 协程:更轻量的并发单元,特别适合高并发网络服务
关键认知:Python的多线程由于GIL存在,实际上同一时刻只有一个线程在执行字节码。但在I/O操作时(如网络请求、文件读写),GIL会被释放,这时多线程才能真正并行。
2. 正则表达式:文本处理的瑞士军刀
去年处理日志分析时,我面对每天20GB的Nginx日志,正则表达式帮我节省了90%的开发时间。比如提取手机号的模式:
import re pattern = r'(?<!\d)(1[3-9]\d{9})(?!\d)' text = "联系方式:13812345678,备用号15698765432" phones = re.findall(pattern, text) # ['13812345678', '15698765432']2.1 正则核心语法速记表
| 元字符 | 作用 | 示例 | 匹配结果 |
|---|---|---|---|
| \d | 数字 | \d{3} | "123" |
| \w | 单词字符 | \w+@\w+.\w+ | "test@qq.com" |
| . | 任意非换行字符 | a.c | "abc"、"a c" |
| * | 0次或多次 | ab*c | "ac","abbc" |
| + | 1次或多次 | a+b | "ab","aab" |
| {n,m} | n到m次重复 | a{2,4} | "aa","aaa" |
| [...] | 字符集合 | [aeiou] | "a","e" |
| (?:...) | 非捕获分组 | (?:ab)+ | "abab" |
2.2 性能优化实战技巧
- 预编译模式:频繁使用的正则应该先compile
phone_re = re.compile(r'\d{11}') # 后续反复使用phone_re对象- 避免回溯灾难:谨慎使用嵌套量词
# 危险写法:可能导致指数级回溯 re.match(r'(a+)+$', 'aaaaaaaaX') # 安全写法 re.match(r'a+$', 'aaaaaaaaX')- 使用原子分组:(?>...)防止回溯
# 匹配引号内容时更高效 re.findall(r'"(?>[^"\\]+|\\.)*"', text)3. Python高级特性:写出优雅代码的秘密
3.1 上下文管理器:资源管理的艺术
处理文件时,这样的代码是不是很眼熟?
f = open('data.txt') try: data = f.read() finally: f.close()用with语句可以简化为:
with open('data.txt') as f: data = f.read()更酷的是我们可以自定义上下文管理器:
class DatabaseConnection: def __enter__(self): self.conn = connect_db() return self.conn def __exit__(self, exc_type, exc_val, exc_tb): self.conn.close() if exc_type: print(f"Error occurred: {exc_val}") # 使用方式 with DatabaseConnection() as db: db.execute("SELECT * FROM users")3.2 装饰器:不修改源码增强函数
记录函数执行时间的通用方案:
import time def timer(func): def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) duration = time.perf_counter() - start print(f"{func.__name__} took {duration:.4f} seconds") return result return wrapper @timer def process_data(size): time.sleep(size * 0.1) process_data(5) # 输出: process_data took 0.5003 seconds3.3 迭代器与生成器:懒加载的智慧
传统列表处理的问题:
def get_big_data(): return [i**2 for i in range(1000000)] # 立即占用大量内存生成器解决方案:
def get_big_data(): for i in range(1000000): yield i**2 # 按需生成 # 使用方式 for num in get_big_data(): if num > 100: break更简洁的生成器表达式:
sum(x*x for x in range(1000000) if x % 2 == 0)4. 并发编程实战:多线程爬虫案例
让我们构建一个获取B站视频信息的并发爬虫:
import threading import requests from queue import Queue class BiliSpider: def __init__(self, worker_num=5): self.task_queue = Queue() self.results = [] self.workers = [] for _ in range(worker_num): t = threading.Thread(target=self.worker) t.daemon = True t.start() self.workers.append(t) def worker(self): while True: bvid = self.task_queue.get() try: url = f"https://api.bilibili.com/x/web-interface/view?bvid={bvid}" resp = requests.get(url).json() self.results.append(resp['data']) except Exception as e: print(f"Error fetching {bvid}: {e}") finally: self.task_queue.task_done() def add_task(self, bvid): self.task_queue.put(bvid) def wait_complete(self): self.task_queue.join() # 使用示例 spider = BiliSpider() for bvid in ['BV1GJ411x7h7', 'BV1QU4y1P7JK']: spider.add_task(bvid) spider.wait_complete() print(f"Got {len(spider.results)} videos")4.1 关键优化点
- 连接池配置:给requests加上Session重用TCP连接
self.session = requests.Session() adapter = requests.adapters.HTTPAdapter( pool_connections=100, pool_maxsize=100 ) self.session.mount('https://', adapter)- 错误重试机制:使用tenacity库自动重试
from tenacity import retry, stop_after_attempt @retry(stop=stop_after_attempt(3)) def fetch_video(bvid): return self.session.get(url).json()- 速率限制:避免被封禁
from time import sleep from random import uniform def worker(self): while True: sleep(uniform(0.5, 1.5)) # 随机延迟 # ...原有逻辑...5. 正则表达式在数据处理中的高阶应用
5.1 日志解析实战
处理Nginx日志的经典案例:
log_pattern = r'(?P<remote_addr>\S+) - \S+ \[(?P<time_local>.*?)\] "(?P<request>.*?)" ' \ r'(?P<status>\d+) (?P<body_bytes_sent>\d+) "(?P<http_referer>.*?)" ' \ r'"(?P<http_user_agent>.*?)"' def parse_log(line): match = re.match(log_pattern, line) if not match: print(f"Failed to parse: {line[:50]}...") return None return match.groupdict() # 示例日志行 log_line = '127.0.0.1 - - [10/Oct/2023:15:32:01 +0800] "GET /api/user HTTP/1.1" 200 2345 "-" "Mozilla/5.0"' print(parse_log(log_line))5.2 数据清洗技巧
处理混乱的用户输入:
def clean_phone(phone): # 去除所有非数字字符 cleaned = re.sub(r'[^\d]', '', phone) # 验证手机号格式 if not re.fullmatch(r'1[3-9]\d{9}', cleaned): raise ValueError(f"Invalid phone number: {phone}") return cleaned print(clean_phone("138-1234 5678")) # "13812345678"5.3 性能敏感场景的优化
当处理GB级文本时,正则可能成为瓶颈。这时可以考虑:
- 字符串原生方法优先:简单判断用startswith/endswith更快
# 慢 if re.match(r'^https://', url): # 快10倍 if url.startswith('https://'):- 使用scanner对象处理大文件:
pattern = re.compile(r'error|warning|critical', re.I) with open('huge.log') as f: scanner = pattern.scanner(f.read()) for match in iter(scanner.match, None): process_error(match.group())- 第三方加速库:如regex模块(支持并行匹配)
import regex # 使用overlapped=True可以找出所有重叠匹配 matches = regex.findall(r'(?=(\d{4}))', '123456', overlapped=True) # 结果: ['1234', '2345', '3456']6. 深入理解Python迭代器协议
6.1 迭代器背后的魔法方法
任何对象只要实现了__iter__()和__next__()方法,就是迭代器:
class CountDown: def __init__(self, start): self.current = start def __iter__(self): return self def __next__(self): if self.current <= 0: raise StopIteration num = self.current self.current -= 1 return num # 使用示例 for num in CountDown(5): print(num) # 输出5,4,3,2,16.2 迭代器工具库itertools
标准库提供了强大的迭代器工具:
from itertools import islice, cycle, chain # 无限循环迭代器 colors = cycle(['red', 'green', 'blue']) # 限制取前5个 limited = islice(colors, 5) print(list(limited)) # ['red', 'green', 'blue', 'red', 'green'] # 连接多个迭代器 combined = chain([1,2], ['a','b'], (x*2 for x in [1,2,3])) print(list(combined)) # [1, 2, 'a', 'b', 2, 4, 6]6.3 生成器的高级用法
生成器可以维护状态并通过send()方法交互:
def running_avg(): total = 0 count = 0 while True: value = yield total/count if count else 0 total += value count += 1 # 使用方式 avg = running_avg() next(avg) # 启动生成器 print(avg.send(10)) # 10.0 print(avg.send(20)) # 15.0 print(avg.send(30)) # 20.07. 并发编程中的陷阱与解决方案
7.1 线程安全问题的经典案例
多线程操作共享数据的危险:
import threading counter = 0 def increment(): global counter for _ in range(100000): counter += 1 threads = [threading.Thread(target=increment) for _ in range(10)] for t in threads: t.start() for t in threads: t.join() print(counter) # 结果可能小于10000007.2 同步原语的选择
| 工具 | 适用场景 | 示例 |
|---|---|---|
| Lock | 简单互斥访问 | 保护共享变量修改 |
| RLock | 可重入锁 | 递归函数中的资源保护 |
| Semaphore | 限制并发数 | 数据库连接池限制 |
| Event | 线程间事件通知 | 主线程通知工作线程启动/停止 |
| Condition | 复杂条件等待 | 生产者-消费者模型 |
正确使用Lock的姿势:
from threading import Lock lock = Lock() counter = 0 def safe_increment(): global counter for _ in range(100000): with lock: # 自动获取和释放锁 counter += 17.3 多进程编程注意事项
- 进程间通信:使用Queue或Pipe
from multiprocessing import Process, Queue def worker(q): q.put('hello from child') q = Queue() p = Process(target=worker, args=(q,)) p.start() print(q.get()) # 'hello from child' p.join()- 共享内存:使用Value/Array
from multiprocessing import Process, Value def increment(shared_num): shared_num.value += 1 num = Value('i', 0) processes = [Process(target=increment, args=(num,)) for _ in range(4)] for p in processes: p.start() for p in processes: p.join() print(num.value) # 4- 进程池最佳实践:
from multiprocessing import Pool def cpu_intensive(n): return sum(i*i for i in range(n)) with Pool(processes=4) as pool: results = pool.map(cpu_intensive, range(1000, 10000, 1000))8. Python元编程技巧
8.1 动态创建类
type()函数的三种用法:
# 1. 获取类型 type(123) # <class 'int'> # 2. 动态创建类 MyClass = type('MyClass', (), {'x': 42}) obj = MyClass() print(obj.x) # 42 # 3. 带方法的类 def say_hello(self): print(f"Hello {self.name}") Person = type('Person', (), { '__init__': lambda self, name: setattr(self, 'name', name), 'greet': say_hello }) p = Person('Alice') p.greet() # Hello Alice8.2 属性访问控制
使用__getattr__实现灵活属性访问:
class DynamicAttributes: def __init__(self): self._data = {} def __getattr__(self, name): if name in self._data: return self._data[name] raise AttributeError(f"No attribute {name}") def __setattr__(self, name, value): if name == '_data': super().__setattr__(name, value) else: self._data[name] = value obj = DynamicAttributes() obj.color = 'red' print(obj.color) # 'red' print(obj.size) # AttributeError8.3 类装饰器的妙用
实现单例模式:
def singleton(cls): instances = {} def wrapper(*args, **kwargs): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return wrapper @singleton class Config: def __init__(self): self.settings = {} c1 = Config() c2 = Config() print(c1 is c2) # True9. 性能优化实战:从正则到C扩展
9.1 正则表达式编译优化
对比不同写法的性能差异:
import re import timeit # 糟糕的写法:每次重新编译 def bad_code(text): return re.match(r'\d+', text) # 良好的写法:预编译 compiled = re.compile(r'\d+') def good_code(text): return compiled.match(text) # 测试性能 text = '123abc' print(timeit.timeit(lambda: bad_code(text), number=100000)) # ~0.25s print(timeit.timeit(lambda: good_code(text), number=100000)) # ~0.08s9.2 使用C扩展加速关键代码
当Python代码成为瓶颈时,可以用Cython或直接写C扩展:
- 创建
fast_regex.c:
#include <Python.h> #include <regex.h> static PyObject* fast_match(PyObject* self, PyObject* args) { const char *pattern, *text; if (!PyArg_ParseTuple(args, "ss", &pattern, &text)) return NULL; regex_t regex; if (regcomp(®ex, pattern, REG_EXTENDED)) return NULL; int result = regexec(®ex, text, 0, NULL, 0); regfree(®ex); return PyBool_FromValue(result == 0); } static PyMethodDef methods[] = { {"fast_match", fast_match, METH_VARARGS, "Fast regex matching"}, {NULL, NULL, 0, NULL} }; PyMODINIT_FUNC PyInit_fast_regex(void) { return PyModule_Create(&(PyModuleDef){ .m_base = PyModuleDef_HEAD_INIT, .m_name = "fast_regex", .m_methods = methods }); }- 编译并安装:
python setup.py build_ext --inplace- Python中使用:
import fast_regex fast_regex.fast_match(r'\d+', '123') # True10. 现代Python并发编程新选择
10.1 asyncio核心概念
异步编程的三要素:
- 事件循环(Event Loop)
- 协程(Coroutines)
- Future/Task对象
基本使用模式:
import asyncio async def fetch_data(url): print(f"Start fetching {url}") await asyncio.sleep(2) # 模拟IO操作 print(f"Finished fetching {url}") return {"url": url, "data": "..."} async def main(): tasks = [ asyncio.create_task(fetch_data("url1")), asyncio.create_task(fetch_data("url2")) ] results = await asyncio.gather(*tasks) print(results) asyncio.run(main())10.2 异步上下文管理器
结合async with使用:
class AsyncDatabase: async def __aenter__(self): self.conn = await connect_db_async() return self.conn async def __aexit__(self, exc_type, exc, tb): await self.conn.close() async def query_data(): async with AsyncDatabase() as db: return await db.execute("SELECT * FROM users")10.3 实际项目中的并发选择
根据场景选择合适方案:
| 场景特征 | 推荐方案 | 原因 |
|---|---|---|
| 大量HTTP请求 | asyncio + aiohttp | 单线程高并发,无GIL限制 |
| CPU密集型计算 | multiprocessing | 利用多核 |
| 简单后台任务 | threading | 实现简单,适合I/O阻塞操作 |
| 需要精细控制 | 协程+回调 | 复杂异步逻辑 |
| 与C/C++扩展交互 | 多进程 | 避免GIL影响扩展模块执行 |
11. 正则表达式调试技巧
11.1 可视化调试工具
使用regex101.com在线测试:
- 输入测试文本和正则模式
- 实时高亮匹配结果
- 查看正则解释和匹配过程
11.2 Python调试技巧
- re.DEBUG标志:查看正则如何被解析
re.compile(r'\d{3}-\d{4}', re.DEBUG) # 输出解析树: # MAX_REPEAT 3 3 # IN # CATEGORY CATEGORY_DIGIT # LITERAL 45 # MAX_REPEAT 4 4 # IN # CATEGORY CATEGORY_DIGIT- 匹配过程追踪:
pattern = re.compile(r'(a|b)*c') pattern.match('ababac').regs # 输出匹配组的位置信息- 性能分析:
import cProfile cProfile.run("re.match(r'(a+)+$', 'aaaaaaaaX')")11.3 常见陷阱与解决方案
- 贪婪匹配问题:
# 想匹配HTML标签内容 html = '<div>content</div>' # 错误写法(贪婪匹配) re.findall(r'<div>(.*)</div>', html) # ['content</div>'] # 正确写法(非贪婪) re.findall(r'<div>(.*?)</div>', html) # ['content']- Unicode匹配:
# 匹配中文 re.findall(r'[\u4e00-\u9fa5]+', '你好Python') # ['你好'] # 匹配emoji re.findall(r'[\U0001F600-\U0001F64F]', 'Hello😊') # ['😊']- 多行模式:
text = """first line second line third line""" # 不启用多行模式 re.findall(r'^.*$', text) # ['first line', 'second line', 'third line'] # 启用多行模式 re.findall(r'^.*$', text, re.MULTILINE) # ['first line', 'second line', 'third line']12. Python高级特性在框架中的应用
12.1 Flask中的装饰器路由
理解Flask路由原理:
routes = {} def route(path): def decorator(f): routes[path] = f return f return decorator @route('/') def home(): return "Hello World" # 模拟请求处理 def handle_request(path): if path in routes: return routes[path]() return "404 Not Found" print(handle_request('/')) # Hello World12.2 Django中的模型元类
Django模型的秘密:
class ModelMeta(type): def __new__(cls, name, bases, attrs): # 自动收集字段 fields = {} for k, v in attrs.items(): if isinstance(v, Field): fields[k] = v attrs['_meta'] = type('Meta', (), {'fields': fields}) return super().__new__(cls, name, bases, attrs) class Field: pass class CharField(Field): def __init__(self, max_length): self.max_length = max_length class User(metaclass=ModelMeta): name = CharField(max_length=100) age = CharField(max_length=3) print(User._meta.fields) # {'name': <__main__.CharField object>, 'age': <__main__.CharField object>}12.3 FastAPI的依赖注入系统
理解依赖注入原理:
from functools import wraps dependencies = {} def inject(name): def decorator(f): @wraps(f) def wrapper(*args, **kwargs): if name in dependencies: kwargs[name] = dependencies[name]() return f(*args, **kwargs) return wrapper return decorator def provide(name): def decorator(f): dependencies[name] = f return f return decorator @provide('db') def get_db(): return "Database Connection" @inject('db') def process_data(db=None): print(f"Using {db}") process_data() # Using Database Connection13. 并发编程中的设计模式
13.1 生产者-消费者模式
使用Queue实现:
import threading import queue import random import time def producer(q, name): for i in range(5): item = f"{name}-{i}" q.put(item) print(f"Produced {item}") time.sleep(random.random()) def consumer(q, name): while True: item = q.get() if item is None: # 终止信号 q.task_done() break print(f"{name} consumed {item}") q.task_done() time.sleep(random.random() * 2) q = queue.Queue() producers = [threading.Thread(target=producer, args=(q, f"P{i}")) for i in range(2)] consumers = [threading.Thread(target=consumer, args=(q, f"C{i}")) for i in range(3)] for t in producers + consumers: t.start() for t in producers: t.join() # 发送终止信号 for _ in consumers: q.put(None) for t in consumers: t.join()13.2 线程池模式
自定义简单线程池:
from queue import Queue from threading import Thread class ThreadPool: def __init__(self, size): self.tasks = Queue() self.workers = [Thread(target=self.worker) for _ in range(size)] for w in self.workers: w.daemon = True w.start() def worker(self): while True: func, args, kwargs = self.tasks.get() try: func(*args, **kwargs) except Exception as e: print(f"Task failed: {e}") finally: self.tasks.task_done() def submit(self, func, *args, **kwargs): self.tasks.put((func, args, kwargs)) def wait_complete(self): self.tasks.join() # 使用示例 pool = ThreadPool(4) for i in range(10): pool.submit(print, f"Task {i}") pool.wait_complete()13.3 发布-订阅模式
使用Condition实现:
from threading import Condition class PubSub: def __init__(self): self.condition = Condition() self.messages = [] self.subscribers = set() def publish(self, message): with self.condition: self.messages.append(message) self.condition.notify_all() def subscribe(self): with self.condition: self.subscribers.add(threading.current_thread().name) while True: if not self.messages: self.condition.wait() while self.messages: yield self.messages.pop(0) # 使用示例 ps = PubSub() def subscriber(name): for msg in ps.subscribe(): print(f"{name} received: {msg}") Thread(target=subscriber, args=("Sub1",), daemon=True).start() Thread(target=subscriber, args=("Sub2",), daemon=True).start() ps.publish("Hello") ps.publish("World") time.sleep(1)14. 正则表达式在Web开发中的应用
14.1 URL路由解析
实现类似Django的路由系统:
import re class Router: def __init__(self): self.routes = [] def add_route(self, pattern, handler): # 将路由模式转换为正则 regex = re.sub(r'\{(\w+)\}', r'(?P<\1>[^/]+)', pattern) self.routes.append((re.compile(f'^{regex}$'), handler)) def match(self, path): for regex, handler in self.routes: match = regex.match(path) if match: return handler, match.groupdict() return None, None router = Router() router.add_route('/users/{id}', 'user_detail') router.add_route('/posts/{year}/{month}', 'post_archive') handler, params = router.match('/users/123') print(handler, params) # 'user_detail' {'id': '123'}14.2 表单验证
邮箱和密码验证:
def validate_email(email): pattern = r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' return bool(re.fullmatch(pattern, email)) def validate_password(password): # 至少8位,包含大小写字母和数字 pattern = r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[\w!@#$%^&*]{8,}$' return bool(re.fullmatch(pattern, password)) print(validate_email("test@example.com")) # True print(validate_password("Passw0rd")) # True14.3 XSS防护
简单的XSS过滤:
def sanitize_html(text): # 移除<script>标签和危险属性 text = re.sub(r'<script[^>]*>.*?</script>', '', text, flags=re.I|re.S) text = re.sub(r'on\w+="[^"]+"', '', text) text = re.sub(r'javascript:', '', text, flags=re.I) return text dirty = '<script>alert(1)</script><img src=x onerror=alert(1)>' print(sanitize_html(dirty)) # '<img src=x >'15. Python并发编程的未来
15.1 结构化并发
使用Trio库的现代并发:
import trio async def child(name): print(f"Child {name} started") await trio.sleep(1) print(f"Child {name} finished") async def parent(): async with trio.open_nursery() as nursery: nursery.start_soon(child, "A") nursery.start_soon(child, "B") print("Parent waiting") trio.run(parent)15.2 异步生成器
处理流式数据:
async def async_counter(max): for i in range(max): await asyncio.sleep(0.1) yield i async def main(): async for num in async_counter(5): print(num) asyncio.run(main())15.3 多线程与协程混合
在协程中运行线程池:
async def run_in_thread(func): loop = asyncio.get_event_loop() return await loop.run_in_executor(None, func) def cpu_bound(): return sum(i*i for i in range(10**6)) async def main(): result = await run_in_thread(cpu_bound) print(f"Result: {result}") asyncio.run(main())