2982 字
15 分钟
Python 札记 (7)
2026-08-30
NOTE

目录骨架来自菜鸟教程 Python3 函数与异常相关章节,表述与细节对照 Python 官方文档 与相关 PEP 核对。基于 Python 3。完整出处见文末 参考

iter 与 next#

迭代器实现迭代器协议:__iter__() 返回自身(或另一迭代器),该对象再提供 __next__()。内置 iter() 优先调用 __iter__();若没有 __iter__ 但实现了从 0 开始的 __getitem__,仍可按序列方式迭代(见 iterator protocol)。日常自己写可迭代对象时,实现 __iter__ 即可。

next(iterator[, default]) 调用 iterator.__next__()。没有更多元素时,__next__() 抛出 StopIteration;若提供了 default,则返回 default 而不抛异常。

容器(如 list)本身通常不是迭代器,但可迭代(iterable):__iter__() 返回一个新的迭代器。每次 for x in lst: 都会先 iter(lst) 再反复 next

it = iter([10, 20, 30])
print(next(it)) # 10
print(next(it)) # 20
print(next(it, "没了")) # 30
print(next(it, "没了")) # 没了

for 循环在底层等价于:取迭代器 → 反复 next → 捕获 StopIteration 结束。自己实现可迭代对象时,最小骨架如下(菜鸟 迭代器 有类似例子,协议以官方为准):

class CountDown:
def __init__(self, n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n <= 0:
raise StopIteration
self.n -= 1
return self.n + 1

生成器与 yield#

生成器是一种迭代器,由含 yield 的函数或 yield from 表达式产生。调用生成器函数不会立刻执行函数体,而是返回一个生成器对象;每次 next()for 循环驱动执行到下一个 yield,产出值并暂停。

def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1
for x in count_up_to(3):
print(x) # 1, 2, 3

生成器表达式 (x * x for x in range(5)) 语法类似列表推导,但结果是生成器而非列表,适合大数据或流水线,见 Generator expressions

yield from iterable 把产出委托给另一个可迭代对象,常用于扁平化嵌套生成器。要用 with 包一层生成器,需 contextlib.contextmanager;生成器本身不是上下文管理器。

定义函数 def#

def 定义用户函数。函数体在调用时才执行;定义时只创建函数对象并绑定名字。函数是一等对象,可赋给变量、当参数、当返回值。

def greet(name):
"""向 name 问好。"""
return f"Hello, {name}!"
print(greet("Alice"))
print(greet.__name__) # greet

教程 · Defining Functions 强调:函数可 return 多个值(实际是返回一个元组),也可无 return(等价于 return None)。

参数:位置、默认、关键字、*args、**kwargs#

参数种类见 More on Defining FunctionsFunction definitions

位置参数(positional):按定义顺序传入。

默认参数(default):形参带 = 默认值;缺省调用时使用默认值。默认值在函数定义时只求值一次——不要用可变对象(如 []{})当默认值,否则多次调用会共享同一对象:

def bad(a, items=[]): # 反例
items.append(a)
return items
def good(a, items=None):
if items is None:
items = []
items.append(a)
return items

仅关键字参数(keyword-only):在 **args 之后的参数必须用关键字传入;PEP 3102 起可在签名里写 *, x, y

关键字参数(keyword)func(a=1, b=2) 按名字传参,顺序可与定义不同。

*args:收集多余位置实参为元组。**kwargs:收集多余关键字实参为字典。名字只是约定,可以是 *rest**opts

def demo(pos, /, both, *, kwonly, **kwargs):
# pos: 仅位置(3.8+ 语法 /)
# both: 位置或关键字
# kwonly: 仅关键字
# kwargs: 其余关键字
print(pos, both, kwonly, kwargs)
demo(1, 2, kwonly=3, extra=4)
# demo(pos=1, ...) 会 TypeError:pos 不能按关键字

调用侧 *iterable 解包位置实参,**mapping 解包关键字实参(键须为字符串)。

return#

return 结束当前函数调用,可选跟一个表达式。无表达式时返回 Nonereturn 可在任意缩进层级结束函数,不会自动结束外层循环(与某些语言不同)。

def find_first(pred, items):
for x in items:
if pred(x):
return x
return None

lambda#

lambda 产生匿名函数对象,语法 lambda 参数: 表达式。只能写单个表达式,不能含语句或赋值(3.8+ 海象 := 在 lambda 里仍受限)。适合短小的回调,复杂逻辑用 def

pairs = [(2, "b"), (1, "a"), (3, "c")]
pairs.sort(key=lambda p: p[0])

LEGB:名字怎么找#

命名与绑定 规定:名字引用按 LEGB 顺序查找——Local(当前函数)、Enclosing(外层函数)、Global(模块全局)、Built-in(内置名,如 len)。

x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print(x) # local
inner()
print(x) # enclosing
outer()
print(x) # global

函数定义在定义时绑定外层作用域(闭包),不是调用时。内层函数可读取外层名字;若要重新绑定外层非全局名字,需要 nonlocal;绑定模块级名字需要 global

if / for / while / try 创建函数那样的局部作用域,块里赋的名在块外仍能用(菜鸟 命名空间 有图;语义以 Naming and binding 为准)。

内置名在 builtins 模块:import builtins; dir(builtins)

global 与 nonlocal#

global 把本函数里的名字标成模块全局nonlocal 标成最近一层外层函数里的名字(不能是全局)。

函数体内只要出现对某名字的赋值,整个函数都把它当局部。因此下面会 UnboundLocalError

x = 1
def f():
print(x) # 想读全局 x
x = 2 # 这一行让 x 在整个 f 里都是局部

要改全局就写 global x,或不要在函数里给同名赋值。

count = 0
def inc_global():
global count
count += 1
def make_counter():
n = 0
def inc():
nonlocal n
n += 1
return n
return inc
c = make_counter()
print(c(), c()) # 1, 2
inc_global()
print(count) # 1

滥用 global 会让数据流难追踪;优先用参数、返回值或类封装状态。

装饰器#

PEP 318 引入函数装饰器语法:@decorator 放在 def(或 class)前,等价于 func = decorator(func)。装饰器通常是接受 callable、返回 callable 的可调用对象。

def log_calls(fn):
def wrapper(*args, **kwargs):
print(f"calling {fn.__name__}")
return fn(*args, **kwargs)
return wrapper
@log_calls
def add(a, b):
return a + b
add(1, 2) # 先打印 calling add,再返回 3

被包装后 wrapper.__name__ 等元数据会丢;用 functools.wraps 拷回去:

import functools
def log_calls(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
print(f"calling {fn.__name__}")
return fn(*args, **kwargs)
return wrapper

带参装饰器:外层函数先接收装饰器参数,返回真正的装饰器。

def repeat(times):
def decorator(fn):
def wrapper(*args, **kwargs):
for _ in range(times):
result = fn(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def hi():
print("hi")

类也可以当装饰器(实现 __call__)。内置装饰器如 staticmethodclassmethodproperty 在第 8 篇类一节会碰到。装饰器栈从下往上应用:@a @b def f 等价于 f = a(b(f))

with 与上下文管理器#

with 语句用于管理资源:进入时 setup,离开时 teardown,即使发生异常也会清理。PEP 343 定义协议。

上下文管理器需实现:

  • __enter__(self):进入 with 块前调用;返回值绑定到 as 后的变量(若无 as 则忽略返回值)。
  • __exit__(self, exc_type, exc_val, exc_tb):离开 with 块时调用。若返回真值,会抑制当前异常(一般不这么做);否则异常继续传播。
class Tag:
def __init__(self, name):
self.name = name
def __enter__(self):
print(f"<{self.name}>")
return self
def __exit__(self, exc_type, exc, tb):
print(f"</{self.name}>")
return False
with Tag("p") as t:
print("body")

内置 open() 返回的文件对象、threading.Lock 等已实现该协议。手写资源管理可用 contextlib.contextmanager 装饰生成器,或用 contextlib.AbstractContextManager。多个上下文管理器可写 with A() as a, B() as b:

try / except / else / finally#

完整语法见 The try statementErrors and Exceptions

try:
risky()
except ValueError as e:
handle(e)
except (TypeError, OSError):
recover()
else:
# 仅当 try 块未抛出异常时执行
on_success()
finally:
# 总是执行(除非进程被杀死等)
cleanup()

要点:

  • except:匹配异常类型(元组表示多个)。as e 绑定异常实例;在 Python 3 里,except 块结束后 e 会被删除,避免循环引用(PEP 3110)。
  • else:没有异常时运行,常把可能抛错的逻辑放在 try、成功路径放在 else,避免把 else 里的错误当成 except 能处理的同一类问题(教程建议)。
  • finally:用于释放资源;若有 returnfinally 仍会在返回前执行。
  • 顺序try → 若异常则匹配的 except → 无异常则 else → 始终 finally

raise 抛出异常:raise ValueError("msg")raise 单独使用则重新抛出当前异常(常在 except 里记录后再抛)。

def parse_int(s):
try:
return int(s)
except ValueError:
raise ValueError(f"不是整数: {s!r}") from None # 抑制异常链

Exception chaining:内层 raise ... from e 保留原因;from None 隐藏上下文。

用户自定义异常:继承 Exception(不要直接继承 BaseException,除非做系统退出类异常)。

class MyAppError(Exception):
pass

assert 用于调试断言,-O 优化模式下会被去掉;不要用 assert 做业务校验或数据验证。

常见内置异常(点名)#

下列名字均来自 Built-in Exceptions,日常读写代码时最常见。完整继承树见文末附录。

异常典型触发场景
Exception几乎所有用户可捕获异常的基类
StopIteration迭代器没有更多元素(next / for 结束)
StopAsyncIteration异步迭代器结束
ArithmeticError算术错误分支的基类
FloatingPointError浮点运算失败(极少见,依赖平台)
OverflowError数值结果太大
ZeroDivisionError除法或取模除零
AssertionErrorassert 失败
AttributeError属性不存在或无法设置
EOFErrorinput() 在无数据可读时(如 Ctrl+D)
ImportErrorimport 失败
ModuleNotFoundError找不到模块(ImportError 子类)
LookupError索引/键错误的基类
IndexError序列下标越界
KeyError字典键不存在
MemoryError内存不足
NameError未定义的名字
UnboundLocalError局部名字在赋值前被引用
OSError系统 / I/O 相关(IOError 是其别名)
FileNotFoundError文件不存在
PermissionError权限不足
RecursionError超过最大递归深度
ReferenceError弱引用对象已销毁(少见)
RuntimeError一般不落在更具体类型时的运行时错误
NotImplementedError抽象方法 / 待实现
SyntaxError语法错误(编译期)
IndentationError缩进错误
TabErrorTab 与空格混用
SystemError解释器内部错误
TypeError类型不对或参数个数不对
ValueError类型对但值不合法(如 int("abc")
UnicodeErrorUnicode 编解码相关基类
UnicodeDecodeError / UnicodeEncodeError解码 / 编码失败
Warning警告类别基类(不是 Exception 同级习惯捕获对象)

一般不捕获 BaseException 下的 KeyboardInterruptSystemExitGeneratorExit,除非在做框架级顶层处理。


附录:内置异常继承关系(节选)#

以下为官方文档 Exception hierarchy 的压缩表,便于查「该捕哪个父类」。缩进表示子类。

基类直接子类(节选)
BaseExceptionSystemExit, KeyboardInterrupt, GeneratorExit, Exception
ExceptionStopIteration, StopAsyncIteration, ArithmeticError, AssertionError, AttributeError, BufferError, EOFError, ImportError, LookupError, MemoryError, NameError, OSError, ReferenceError, RuntimeError, SyntaxError, SystemError, TypeError, ValueError, Warning
ArithmeticErrorFloatingPointError, OverflowError, ZeroDivisionError
LookupErrorIndexError, KeyError
OSErrorBlockingIOError, ChildProcessError, ConnectionError, FileExistsError, FileNotFoundError, InterruptedError, IsADirectoryError, NotADirectoryError, PermissionError, ProcessLookupError, TimeoutError
ConnectionErrorBrokenPipeError, ConnectionAbortedError, ConnectionRefusedError, ConnectionResetError
ImportErrorModuleNotFoundError
NameErrorUnboundLocalError
RuntimeErrorNotImplementedError, RecursionError
SyntaxErrorIndentationErrorTabError
UnicodeErrorUnicodeDecodeError, UnicodeEncodeError, UnicodeTranslateError
ValueErrorUnicodeError

捕获时:except OSError 可捕 FileNotFoundErrorexcept LookupError 可捕 IndexErrorKeyError。具体是否捕获取决于你是否能合理处理。


附录:函数参数汇总#

写法含义
def f(a, b)位置或关键字
def f(a, b=0)b 有默认值
def f(a, /, b)a 仅位置(3.8+)
def f(a, *, b)b 仅关键字
def f(*args)多余位置 → 元组
def f(**kwargs)多余关键字 → 字典
def f(a, /, b, *args, c=1, **kw)顺序:仅位置、/、位置或关键字、*args、仅关键字、**kwargs

参考#

官方文档(中文版 可对照阅读):

  1. More Control Flow Tools — Defining Functions — 默认参数、关键字参数、*args / **kwargs、Docstring
  2. More Control Flow Tools — Intermezzo: Coding Style — 命名与可读性(与函数组织相关)
  3. Errors and Exceptions — 异常处理、else / finallyraise、用户异常、异常链
  4. Compound statements — Function definitions — 完整参数语法、lambda 不能替代 def 的全部能力
  5. Compound statements — The try statementtry / except / else / finally 语义
  6. Compound statements — The with statementwithas
  7. Data model — With Statement Context Managers__enter__ / __exit__
  8. Glossary — iterator / generator / iterable — 术语定义
  9. Execution model — Naming and binding — LEGB、闭包
  10. Simple statements — global / nonlocal / return / raise / yield
  11. Built-in Exceptions — 异常列表与继承树
  12. functools.wraps — 装饰器保留元数据
  13. contextlib — 上下文管理器工具

PEP:

  1. PEP 318 — Decorators for Functions and Methods
  2. PEP 343 — The “with” Statement
  3. PEP 3102 — Keyword-Only Arguments
  4. PEP 3110 — Catching exceptions in Python 3000

目录来源(骨架,不以它为准改语法):

  1. 菜鸟教程 · Python3 函数
  2. 菜鸟教程 · Python3 迭代器与生成器
  3. 菜鸟教程 · Python3 装饰器
  4. 菜鸟教程 · Python3 错误和异常
  5. 菜鸟教程 · Python3 命名空间/作用域
  6. 菜鸟教程 · Python3 with
  7. 菜鸟教程 · Python3 lambda
Python 札记 (7)
https://blog.chuwu.top/posts/python/python7/
作者
ChuwuYo
发布于
2026-08-30
许可协议
CC BY-NC-SA 4.0