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),无异常则为 None
  • exc_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, dictiter(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@propertyobj.attr是 必需
setter@attr.setterobj.attr = value否 可选
deleter@attr.deleterdel 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 中做耗时操作(除非必要且有缓存)
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐