mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
1558 字
4 分钟
Python 进阶指南
2020-09-01

凌晨两点,线上服务突然报警:某个 API 接口响应时间从 50ms 飙升到 5s。排查后发现,一个同事在处理百万级数据列表时用了列表推导而非生成器,导致内存瞬间占满,GC 频繁触发。改用生成器表达式后,内存占用从 2GB 降到几 MB,响应时间恢复正常。这类问题在 Python 后端开发中并不少见——掌握装饰器、生成器、上下文管理器、类型注解等进阶特性,不仅能写出更优雅的代码,还能在关键时刻快速定位和解决性能瓶颈。

一、装饰器#

1.1 装饰器基础#

装饰器是 Python 最优雅的语法特性之一,本质是一个接受函数并返回函数的高阶函数(Higher-Order Function)

import functools
def timer(func):
"""计时装饰器:测量函数执行时间"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time() - start:.2f}s")
return result
return wrapper
@timer
def slow_function():
import time
time.sleep(1)
slow_function() # 输出: slow_function took 1.00s

@functools.wraps(func) 不能省略,它把原函数的 __name____doc__ 等元信息复制到 wrapper 上。否则调试时看到的函数名都是 wrapper

1.2 带参数的装饰器#

带参数的装饰器需要多一层嵌套,外层函数接收装饰器参数,内层函数才是真正的装饰器:

def retry(max_attempts=3, delay=1.0, exceptions=(Exception,)):
"""重试装饰器:失败自动重试"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
import time
for attempt in range(max_attempts):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt == max_attempts - 1:
raise
print(f"Retry {attempt + 1}/{max_attempts}: {e}")
time.sleep(delay)
return wrapper
return decorator
# 使用默认参数
@retry()
def unstable_api_call():
pass
# 自定义重试策略
@retry(max_attempts=5, delay=2.0, exceptions=(ConnectionError, TimeoutError))
def fetch_data(url):
pass

1.3 类装饰器#

当装饰器需要维护状态时,类装饰器比函数装饰器更清晰:

class CountCalls:
"""统计函数调用次数"""
def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
print(f"{self.func.__name__} called {self.count} times")
return self.func(*args, **kwargs)
@CountCalls
def say_hello(name):
return f"Hello, {name}"
say_hello("Alice") # say_hello called 1 times
say_hello("Bob") # say_hello called 2 times

1.4 常用装饰器模式#

模式用途示例
日志记录记录函数调用@log(level="INFO")
权限校验验证用户权限@require_role("admin")
缓存缓存函数结果@functools.lru_cache(maxsize=128)
重试失败自动重试@retry(max_attempts=3)
单例确保只创建一个实例@singleton
类型校验运行时类型检查@typecheck

二、生成器与迭代器#

2.1 生成器函数#

生成器使用 yield 暂停函数执行,每次调用 next() 恢复到上次暂停的位置。这种**惰性计算(Lazy Evaluation)**的方式可以处理无限序列或超大文件:

def fibonacci(n):
"""斐波那契生成器"""
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
# 使用
for i in fibonacci(10):
print(i)
# 生成器表达式(比列表推导更省内存)
squares = (x * x for x in range(1_000_000))
# 列表推导 [x * x for x in range(1_000_000)] 会立即分配内存

2.2 生成器高级用法#

def read_large_file(filepath, chunk_size=8192):
"""按块读取大文件,不会一次性加载到内存"""
with open(filepath, 'r') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk
def csv_rows(filepath):
"""逐行解析 CSV,生成字典"""
import csv
with open(filepath, 'r') as f:
reader = csv.DictReader(f)
for row in reader:
yield row
# 管道式处理:生成器链
def filter_active(users):
for user in users:
if user['active']:
yield user
def extract_names(users):
for user in users:
yield user['name']
# 串联生成器
active_names = extract_names(filter_active(csv_rows('users.csv')))

2.3 yield from#

yield from 用于将一个生成器的输出委托给另一个生成器:

def flatten(nested):
"""递归展平嵌套列表"""
for item in nested:
if isinstance(item, (list, tuple)):
yield from flatten(item)
else:
yield item
list(flatten([1, [2, [3, 4]], 5])) # [1, 2, 3, 4, 5]
# yield from 也可以用于子生成器通信
def accumulator():
total = 0
while True:
value = yield total
if value is None:
break
total += value
def delegator():
result = yield from accumulator()
print(f"Total: {result}")
gen = delegator()
next(gen) # 启动生成器
gen.send(10) # 返回 10
gen.send(20) # 返回 30
gen.send(None) # 打印 Total: 30, 抛出 StopIteration

2.4 迭代器协议#

自定义迭代器需要实现 __iter____next__

class Range:
def __init__(self, start, stop=None, step=1):
if stop is None:
start, stop = 0, start
self.current = start
self.stop = stop
self.step = step
def __iter__(self):
return self
def __next__(self):
if self.step > 0 and self.current >= self.stop:
raise StopIteration
if self.step < 0 and self.current <= self.stop:
raise StopIteration
value = self.current
self.current += self.step
return value
# 使用
for i in Range(5): # 0, 1, 2, 3, 4
print(i)
for i in Range(2, 10, 3): # 2, 5, 8
print(i)

2.5 生成器 vs 列表对比#

特性列表生成器
内存一次性分配惰性生成,几乎不占内存
速度首次创建慢首次创建快
遍历次数可重复遍历只能遍历一次
索引访问支持 O(1)不支持
适用场景数据量小,需多次访问数据量大,单次遍历

三、上下文管理器#

3.1 文件操作#

with open('file.txt', 'r') as f:
content = f.read()
# 自动关闭文件,即使发生异常

3.2 自定义上下文管理器#

class Timer:
"""计时上下文管理器"""
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
print(f"Elapsed: {time.time() - self.start:.2f}s")
return False # 不吞掉异常
with Timer() as t:
# 操作
pass

3.3 contextmanager 装饰器#

contextlib.contextmanager 让你用生成器写上下文管理器,代码更简洁:

from contextlib import contextmanager
@contextmanager
def timer(name="Block"):
import time
start = time.time()
try:
yield
finally:
print(f"{name} took {time.time() - start:.2f}s")
with timer("Database query"):
# 执行数据库查询
pass
# 更实用的例子:临时切换工作目录
@contextmanager
def change_dir(path):
import os
old_dir = os.getcwd()
os.chdir(path)
try:
yield
finally:
os.chdir(old_dir)
with change_dir('/tmp'):
# 在 /tmp 下执行操作
pass

3.4 常用上下文管理器#

from contextlib import suppress, redirect_stdout
import io
# suppress:忽略指定异常
with suppress(FileNotFoundError):
os.remove('nonexistent.txt') # 文件不存在也不会报错
# redirect_stdout:重定向标准输出
f = io.StringIO()
with redirect_stdout(f):
print("Hello")
output = f.getvalue() # "Hello\n"

四、类型注解#

4.1 基本类型#

def greet(name: str) -> str:
return f"Hello, {name}"
def process(items: list[int], config: dict[str, str]) -> tuple[bool, str]:
return True, "success"
# 可选类型
from typing import Optional
def find_user(user_id: int) -> Optional[str]:
# 返回 str 或 None
return None

4.2 泛型与 Protocol#

from typing import TypeVar, Protocol, Generic
T = TypeVar('T')
class Readable(Protocol):
def read(self) -> bytes: ...
def load(reader: Readable) -> bytes:
return reader.read()
# 泛型容器
class Stack(Generic[T]):
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
stack: Stack[int] = Stack()
stack.push(42)

4.3 TypedDict 与 Literal#

from typing import TypedDict, Literal
# 精确定义字典结构
class UserInfo(TypedDict):
name: str
age: int
email: str
def create_user(info: UserInfo) -> None:
print(info['name'])
# 限制取值范围
Mode = Literal['read', 'write', 'execute']
def set_permission(mode: Mode) -> None:
pass
set_permission('read') # OK
set_permission('delete') # mypy 报错

4.4 TypeGuard 与类型收窄#

from typing import TypeGuard
def is_str_list(val: list[object]) -> TypeGuard[list[str]]:
return all(isinstance(x, str) for x in val)
def process(items: list[object]):
if is_str_list(items):
# 在这个分支里,mypy 知道 items 是 list[str]
print(items[0].upper())

4.5 类型注解工具链#

工具用途安装
mypy静态类型检查pip install mypy
pyright微软出品的类型检查器npm install pyright
pydantic运行时数据验证pip install pydantic
typeguard运行时类型检查pip install typeguard

五、dataclass 与 attrs#

5.1 dataclass 基础#

dataclass 自动生成 __init____repr____eq__ 等方法,减少样板代码:

from dataclasses import dataclass, field
@dataclass
class User:
name: str
email: str
age: int = 0 # 默认值
tags: list[str] = field(default_factory=list) # 可变默认值必须用 factory
id: int = field(init=False) # 不在 __init__ 中
def __post_init__(self):
# __init__ 执行后自动调用
self.id = hash(self.email)
user = User(name="Alice", email="alice@example.com", age=30)
print(user) # User(name='Alice', email='alice@example.com', age=30, tags=[], id=...)

5.2 frozen dataclass#

@dataclass(frozen=True)
class Point:
x: float
y: float
p = Point(1.0, 2.0)
# p.x = 3.0 # 会抛出 FrozenInstanceError
# frozen dataclass 可以作为字典键或放入 set

5.3 dataclass vs 普通类 vs attrs#

特性普通类dataclassattrs
样板代码
不可变支持手动实现frozen=Truefrozen=True
验证器手动实现post_init内置
转换为字典手动实现asdict()asdict()
继承支持需注意字段顺序
第三方依赖无(标准库)需安装 attrs

六、元类#

6.1 元类基础#

元类是创建类的类。type 是 Python 的默认元类:

# class MyClass 等价于
MyClass = type('MyClass', (), {})
# 自定义元类
class SingletonMeta(type):
"""单例元类"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self):
self.connected = True
db1 = Database()
db2 = Database()
print(db1 is db2) # True

6.2 __init_subclass__#

Python 3.6+ 提供了 __init_subclass__ 钩子,比元类更简单:

class RegisteredBase:
"""自动注册子类"""
_registry = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
RegisteredBase._registry[cls.__name__] = cls
class HandlerA(RegisteredBase):
pass
class HandlerB(RegisteredBase):
pass
print(RegisteredBase._registry)
# {'HandlerA': <class 'HandlerA'>, 'HandlerB': <class 'HandlerB'>}

6.3 元类适用场景#

场景推荐方式说明
单例模式模块级变量或装饰器元类过重
子类注册__init_subclass__比元类更清晰
ORM 模型定义元类Django ORM 就用了元类
接口强制元类 + ABC抽象基类
API 框架路由注册元类或装饰器看团队偏好

七、异步编程#

7.1 async/await 基础#

import asyncio
async def fetch_data(url: str) -> str:
"""模拟异步 HTTP 请求"""
await asyncio.sleep(1) # 模拟 IO 等待
return f"Data from {url}"
async def main():
# 串行执行:总共 3 秒
result1 = await fetch_data("https://api1.com")
result2 = await fetch_data("https://api2.com")
result3 = await fetch_data("https://api3.com")
# 并发执行:总共 1 秒
results = await asyncio.gather(
fetch_data("https://api1.com"),
fetch_data("https://api2.com"),
fetch_data("https://api3.com"),
)
asyncio.run(main())

7.2 异步迭代器与生成器#

async def async_read_lines(filepath):
"""异步逐行读取文件"""
import aiofiles
async with aiofiles.open(filepath, 'r') as f:
async for line in f:
yield line.strip()
async def process_logs():
async for line in async_read_lines('/var/log/app.log'):
if 'ERROR' in line:
print(line)

7.3 异步上下文管理器#

import aiohttp
async def fetch_json(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.json()

7.4 asyncio 高级模式#

import asyncio
async def producer(queue: asyncio.Queue):
for i in range(10):
await queue.put(i)
print(f"Produced: {i}")
await asyncio.sleep(0.1)
async def consumer(queue: asyncio.Queue, name: str):
while True:
item = await queue.get()
print(f"Consumer {name} got: {item}")
queue.task_done()
async def main():
queue = asyncio.Queue(maxsize=5)
# 启动生产者和消费者
await asyncio.gather(
producer(queue),
consumer(queue, "A"),
consumer(queue, "B"),
)
asyncio.run(main())

7.5 同步 vs 异步选择#

场景推荐理由
CPU 密集型多进程GIL 限制,异步无优势
IO 密集型异步等待 IO 时不阻塞其他任务
混合型异步+进程池asyncio + ProcessPoolExecutor
已有同步代码多线程改造成本低
全新项目异步原生支持,性能更优

八、性能优化#

8.1 性能分析#

# cProfile 使用
import cProfile
cProfile.run('main()', sort='cumulative')
# 行级分析
# pip install line_profiler
@profile
def hot_function():
pass
# 内存分析
# pip install memory_profiler
@profile
def memory_heavy():
big_list = [i for i in range(1000000)]
return sum(big_list)

8.2 优化技巧#

技巧说明示例
列表推导比手写循环快 20%-30%[x*2 for x in range(100)]
set 查找O(1) vs list 的 O(n)x in my_set
lru_cache缓存纯函数结果@functools.lru_cache(128)
__slots__减少内存占用,禁止动态属性__slots__ = ['x', 'y']
itertools惰性迭代,避免中间列表itertools.chain(a, b)
局部变量局部变量访问比全局变量快函数内缓存 len = len
字符串拼接join 比 + 快','.join(parts)
生成器处理大数据不占内存(x for x in range(10**6))

8.3 __slots__ 实战#

class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
# 普通 class 每个实例有 __dict__,占用约 56 字节
# 使用 __slots__ 的实例约 16 字节
# 创建 100 万个实例可以节省约 40MB 内存
# 缺点:不能动态添加属性
# p = Point(1, 2)
# p.z = 3 # AttributeError

8.4 性能优化流程#

  1. 先分析,再优化:用 cProfile 找到真正的瓶颈
  2. 算法优先:O(n) 到 O(log n) 的改进远大于微优化
  3. 选择合适数据结构:set 代替 list 做成员检查
  4. 减少不必要的计算:缓存、惰性求值
  5. 并行化:CPU 密集用多进程,IO 密集用异步
  6. C 扩展:性能关键路径用 Cython 或 C 扩展

九、总结#

Python 进阶核心能力可以归纳为以下层级:

层级技能价值
函数增强装饰器、闭包、高阶函数减少重复代码,增强可读性
惰性计算生成器、迭代器、itertools处理大数据,节省内存
资源管理上下文管理器、with 语句安全释放资源
类型安全type hints、Protocol、TypedDict提升代码可靠性和 IDE 支持
数据建模dataclass、attrs减少样板代码
元编程元类、描述符、init_subclass框架级抽象
并发编程asyncio、Queue、gatherIO 密集型场景性能翻倍

参考资料#

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

Python 进阶指南
https://blog.souloss.com/posts/language/language-python-advanced/
作者
Souloss
发布于
2020-09-01
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时