Python高级语法
Python高级语法
上下文管理器
上下文管理器是一种协议对象,用于在执行一段代码块前后自动执行设置和清理操作。最典型的使用场景是资源管理(如文件、锁、数据库连接等),确保资源被正确释放,即使发生异常也不会泄漏。
核心作用:“自动善后” —— 进入时准备,退出时清理。
基本语法:
with context_manager as variable:
# 执行受保护的代码块
pass
等价于(伪代码):
context = context_manager.__enter__()
try:
variable = context
# 执行代码块
finally:
context_manager.__exit__(exc_type, exc_val, exc_tb)
即使代码块中抛出异常,
__exit__也会被调用!
上下文管理器协议
一个对象要成为上下文管理器,必须实现两个方法:
| 方法 | 说明 |
|---|---|
__enter__(self) | 进入 with 块时调用,返回值赋给 as 后的变量 |
__exit__(self, exc_type, exc_val, exc_tb) | 退出 with 块时调用,处理异常或清理资源 |
__exit__ 参数说明:
exc_type:异常类型(如ValueError),无异常则为Noneexc_val:异常实例exc_tb:异常的 traceback 对象
如果
__exit__返回True,表示异常已被处理,不会向外传播。
自定义上下文管理器
场景:文件编辑
class MyFile:
def __init__(self, file_name, file_model):
self.fileName = file_name
self.fileModel = file_model
self.file = None
def __enter__(self):
self.file = open(self.fileName, self.fileModel)
return self.file # 通常返回 self 或有用的对象
def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()
# 返回 None(默认),异常会继续抛出
if __name__ == '__main__':
# 写文件
with MyFile('a.txt', 'w') as src_file:
src_file.write('hello world')
场景:抑制特定异常
class Suppress:
def __init__(self, *exception_types):
self.exception_types = exception_types
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type and issubclass(exc_type, self.exception_types):
print(f"忽略异常: {exc_val}")
return True # 抑制异常
return False
# 使用
with Suppress(ValueError, TypeError):
int("abc") # 不会报错!
print("继续执行")
使用 @contextmanager 装饰器(推荐!)
from contextlib import contextmanager
@contextmanager
def open_file(filename, mode='r'):
print(f"打开文件: {filename}")
f = open(filename, mode)
try:
yield f # yield 之前的代码 = __enter__
finally:
f.close() # yield 之后的代码 = __exit__
print(f"关闭文件: {filename}")
# 使用
with open_file('test.txt', 'w') as f:
f.write("Hello")
# 自动关闭,即使写入出错
常见内置上下文管理器
| 用途 | 示例 |
|---|---|
| 文件操作 | with open('file.txt') as f: |
| 线程锁 | with threading.Lock(): |
| 临时修改状态 | with decimal.localcontext() as ctx: |
| 临时目录 | with tempfile.TemporaryDirectory() as tmpdir: |
| 数据库事务 | with connection: # 自动 commit/rollback |
注意事项
- 不要滥用:简单操作无需上下文管理器
__exit__必须处理异常参数,即使不使用yield在@contextmanager中只能出现一次- 避免在
__exit__中抛出新异常,会掩盖原始异常
迭代器
迭代器(Iterator)是 Python 中的一种对象,用于在数据集合中逐个访问元素,而不需要暴露数据集合的底层实现。它提供了一种遍历集合元素的标准方式,适用于任何支持迭代的数据结构
迭代器是实现了 迭代器协议 的对象,该协议包含两个核心方法:
__iter__():返回迭代器对象本身(通常就是self)__next__():返回容器中的下一个元素;如果没有更多元素,则抛出StopIteration异常
所有迭代器都是可迭代对象(Iterable),但不是所有可迭代对象都是迭代器。
可迭代对象 vs 迭代器
| 特性 | 可迭代对象(Iterable) | 迭代器(Iterator) |
|---|---|---|
是否实现 __iter__() | 是 | 是 |
是否实现 __next__() | 否 | 是 |
能否被 next() 调用 | 否 | 是 |
| 是否可重复遍历 | 是(如 list、str) | 否(一次性消耗) |
| 示例 | list, tuple, str, dict | iter(list), map(), zip() |
# 示例:list 是可迭代对象,但不是迭代器
lst = [1, 2, 3]
print(hasattr(lst, '__iter__')) # True
print(hasattr(lst, '__next__')) # False
# 获取它的迭代器
it = iter(lst)
print(hasattr(it, '__next__')) # True
print(next(it)) # 1
print(next(it)) # 2
自定义迭代器
class CountDown:
def __init__(self, start):
self.start = start
def __iter__(self):
return self
def __next__(self):
if self.start <= 0:
raise StopIteration
self.start -= 1
return self.start + 1
# 使用
for num in CountDown(3):
print(num) # 输出: 3, 2, 1
注意:
__iter__必须返回一个迭代器对象(通常是self),__next__控制每次迭代返回的值。
内置函数与迭代器
| 函数 | 返回类型 | 说明 |
|---|---|---|
iter(obj) | Iterator | 将可迭代对象转为迭代器 |
next(it) | 元素 / 抛异常 | 获取迭代器下一个元素 |
list(it) | list | 消耗迭代器生成列表 |
enumerate(iterable) | Iterator | 返回 (index, value) 对 |
map(func, iterable) | Iterator | 映射函数到每个元素 |
filter(func, iterable) | Iterator | 过滤满足条件的元素 |
zip(*iterables) | Iterator | 并行迭代多个序列 |
# 示例
numbers = [1, 2, 3]
squared = map(lambda x: x**2, numbers) # 返回迭代器
print(list(squared)) # [1, 4, 9]
迭代器的优点
- 内存高效:不一次性加载所有数据(惰性求值)
- 适用于大数据流:如文件逐行读取、网络数据流
- 支持无限序列:如自然数序列
# 无限迭代器示例(需手动终止)
def natural_numbers():
n = 1
while True:
yield n
n += 1
# 或使用 itertools
import itertools
nums = itertools.count(1) # 1, 2, 3, ...
常见误区
误区1:认为 for 循环直接作用于可迭代对象
实际上:for item in obj 会先调用 iter(obj) 获取迭代器,再不断调用 next() 直到 StopIteration
误区2:迭代器可以重复使用
实际上:迭代器是一次性的,用完即“耗尽”
it = iter([1, 2, 3])
list(it) # [1, 2, 3]
list(it) # [] —— 已耗尽!
总结
可迭代有
__iter__,迭代器还要__next__;for 循环靠它俩,用完就废别想复用。
在处理大型数据集时,优先使用迭代器/生成器以节省内存!
生成器
生成器(Generator) 是一种特殊的迭代器,它能惰性地(lazy)生成值,而不是一次性将所有值存储在内存中。
核心优势:节省内存,适用于处理大数据流、无限序列或昂贵计算。
生成器有两种创建方式:
- 生成器函数:使用
yield关键字的函数 - 生成器表达式:类似列表推导式,但用圆括号
()包裹
生成器函数
基本语法:
def my_generator():
yield 1
yield 2
yield 3
特点
- 调用生成器函数 不会立即执行函数体,而是返回一个生成器对象
- 每次调用
next()或进入for循环,函数从上次yield处恢复执行 - 遇到
return或函数结束时,自动抛出StopIteration
def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
gen = count_up_to(3)
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
# print(next(gen)) # StopIteration
执行流程:
第一次next()→ 执行到第一个yield→ 返回值并暂停
第二次next()→ 从暂停处继续 → 到下一个yield→ 再次暂停
生成器表达式(Generator Expression)
类似列表推导式,但用 () 而不是 []:
# 列表推导式(立即生成全部)
squares_list = [x**2 for x in range(10)] # 占用内存
# 生成器表达式(惰性求值)
squares_gen = (x**2 for x in range(10)) # 几乎不占内存
print(type(squares_gen)) # <class 'generator'>
print(list(squares_gen)) # [0, 1, 4, 9, ..., 81]
适用场景:只需遍历一次的数据(如
sum(),max(),for循环)
生成器 vs 普通函数 vs 列表
| 特性 | 普通函数 | 列表 | 生成器 |
|---|---|---|---|
| 返回值 | 一次性返回 | 一次性生成所有元素 | 按需逐个生成 |
| 内存占用 | 低 | 高(O(n)) | 极低(O(1)) |
| 可重复遍历 | 是 | 是 | 否(一次性消耗) |
支持 next() | 否 | 否 | 是 |
| 适合大数据 | 否 | 否 | 是 |
高级特性
yield from —— 委托子生成器
用于简化嵌套生成器的写法:
def gen1():
yield 1
yield 2
def gen2():
yield from gen1() # 等价于 for x in gen1(): yield x
yield 3
list(gen2()) # [1, 2, 3]
生成器的 .send() 方法 —— 双向通信
生成器不仅可以产出值,还能接收外部输入:
def echo():
while True:
received = yield
print(f"收到: {received}")
g = echo()
next(g) # 启动生成器(执行到第一个 yield)
g.send("Hello") # 输出: 收到: Hello
g.send("World") # 输出: 收到: World
注意:首次必须调用
next()或send(None)启动!
带返回值的双向生成器
def accumulator():
total = 0
while True:
value = yield total
if value is not None:
total += value
acc = accumulator()
next(acc) # 启动,返回 0
print(acc.send(5)) # 5
print(acc.send(3)) # 8
.throw() 和 .close()
gen.throw(exc):在生成器内部抛出异常gen.close():提前终止生成器(会触发GeneratorExit)
def demo():
try:
yield "start"
except ValueError as e:
yield f"捕获异常: {e}"
finally:
print("清理资源")
g = demo()
print(next(g)) # "start"
print(g.throw(ValueError("出错了"))) # "捕获异常: 出错了"
# 输出: 清理资源
案例:
大文件逐行处理
def read_large_file(file_path):
with open(file_path) as f:
for line in f:
yield line.strip()
# 内存只保留一行
for line in read_large_file('huge.log'):
process(line)
总结
函数带 yield,返回生成器;惰性求值省内存,用完即废别复用。send 双向传数据,yield from 委托好帮手!
最佳实践:
- 处理大数据时优先考虑生成器
- 用生成器表达式替代简单列表推导式(当只需遍历一次)
- 利用
itertools+ 生成器构建高效数据管道
Iterator VS Generator
核心关系一句话总结:
生成器 是 迭代器 的一种特例。
所有生成器都是迭代器,但并非所有迭代器都是生成器。
定义对比
| 概念 | 定义 |
|---|---|
| 迭代器(Iterator) | 实现了 __iter__() 和 __next__() 方法的对象,用于逐个访问容器中的元素 |
| 生成器(Generator) | 一种特殊的迭代器,通过函数中使用 yield 或生成器表达式自动创建,具备惰性求值和状态保存能力 |
创建方式对比
| 类型 | 创建方式 | 示例 |
|---|---|---|
| 迭代器 | 手动实现 __iter__ 和 __next__ 的类 | python<br>class MyIter:<br> def __iter__(self): return self<br> def __next__(self): ... |
| 生成器 | ① 带 yield 的函数 ② 生成器表达式 (x for x in ...) | python<br>def gen(): yield 1<br><br>squares = (x2 for x in range(5)) |
生成器由 Python 自动实现迭代器协议,无需手动写
__next__。
功能与特性对比
| 特性 | 迭代器(Iterator) | 生成器(Generator) |
|---|---|---|
是否自动实现 __iter__ / __next__ | 否 需手动实现 | 是 自动实现 |
| 是否支持惰性求值 | 是(通常) | 是(天然支持) |
| 是否能保存执行状态 | 否 需自行维护状态(如用实例变量) | 是 自动挂起/恢复函数状态 |
是否支持双向通信(.send()) | 否 不支持 | 是 支持 .send(), .throw(), .close() |
| 内存效率 | 取决于实现 | 极高(按需生成) |
| 编写复杂度 | 较高(需处理异常、状态等) | 极低(一行 yield 即可) |
| 适用场景 | 复杂控制逻辑、面向对象设计 | 数据流处理、管道、简单序列生成 |
代码示例对比
相同功能:生成 1 到 3 的数字
迭代器实现(繁琐):
class NumberIterator:
def __init__(self, start=1, end=3):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current > self.end:
raise StopIteration
value = self.current
self.current += 1
return value
it = NumberIterator()
print(list(it)) # [1, 2, 3]
生成器实现(简洁):
def number_generator(start=1, end=3):
current = start
while current <= end:
yield current
current += 1
gen = number_generator()
print(list(gen)) # [1, 2, 3]
生成器版本更短、更易读、自动处理
StopIteration!
状态管理对比
- 迭代器:必须用实例属性(如
self.current)手动跟踪状态 - 生成器:函数局部变量在
yield后自动“冻结”,下次调用时恢复
总结:
记住:
“能用生成器,就别手写迭代器” —— 更简洁、更安全、更 Pythonic!
property属性
property 是 Python 的内置函数/装饰器,用于将类中的方法“伪装”成属性(attribute),使得在访问、赋值或删除时自动触发对应的方法,实现数据封装与验证。
核心目的:控制对私有/受保护属性的访问,同时保持简洁的点号(
.)语法。
基本语法
使用 @property 装饰器(推荐!)
class Circle:
def __init__(self, radius):
self._radius = radius # 使用下划线表示“受保护”
@property
def radius(self):
"""getter:获取半径"""
return self._radius
@radius.setter
def radius(self, value):
"""setter:设置半径(带验证)"""
if value < 0:
raise ValueError("半径不能为负数")
self._radius = value
@radius.deleter
def radius(self):
"""deleter:删除半径"""
print("删除半径属性")
del self._radius
使用示例:
c = Circle(5)
print(c.radius) # 5 ← 像访问属性一样调用 getter
c.radius = 10 # 触发 setter(自动验证)
# c.radius = -1 # 抛出 ValueError
del c.radius # 触发 deleter
关键:用户无需知道背后是方法还是变量,接口保持一致!
使用 property() 函数(较少用)
class Circle:
def __init__(self, radius):
self._radius = radius
def get_radius(self):
return self._radius
def set_radius(self, value):
if value < 0:
raise ValueError("半径不能为负数")
self._radius = value
def del_radius(self):
del self._radius
# property(fget, fset, fdel, doc)
radius = property(get_radius, set_radius, del_radius, "半径属性")
装饰器方式本质是
property函数的语法糖。
property 的三大组件
| 组件 | 装饰器 | 对应操作 | 是否必需 |
|---|---|---|---|
| getter | @property | obj.attr | 是 必需 |
| setter | @attr.setter | obj.attr = value | 否 可选 |
| deleter | @attr.deleter | del obj.attr | 否 可选 |
如果只定义 getter,则属性为只读!
class Person:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
p = Person("Alice")
print(p.name) # Alice
# p.name = "Bob" # AttributeError: can't set attribute
应用场景
场景1:数据验证
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("温度低于绝对零度!")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32 # 只读计算属性
场景2:懒加载(Lazy Evaluation)
class DataProcessor:
def __init__(self):
self._expensive_data = None
@property
def data(self):
if self._expensive_data is None:
print("正在加载大数据...")
self._expensive_data = self._load_data()
return self._expensive_data
def _load_data(self):
return [i**2 for i in range(1000000)]
总结
属性想控制,
@property来相助;getter必须有,setter/deleter看需求。数据要验证,懒加载也行,接口不变迁,代码更健壮!
最佳实践:
- 优先使用
@property装饰器(而非property()函数) - 存储用
_xxx,暴露用xxx - 只读属性省略 setter
- 避免在 property 中做耗时操作(除非必要且有缓存)
更多推荐


所有评论(0)