第 1 周周测答案(默认折叠)¶
闭卷做完、自己判完再看
点击展开
第 1 周周测答案¶
所有输出已在 Python 3.14.4 上实际运行核对。
一、预测输出¶
14 0—— 生成器只能遍历一次,第二次sum时已经耗尽。1 [2];"done"成了StopIteration.value,list()会静默吞掉它;只有yield from gen()这样的委托才能把它当作表达式的值拿到。[2, 2, 2] [0, 1, 2]—— 闭包晚绑定:lambda: i在调用时才查i,此时循环已结束、i == 2;默认参数i=i在定义时就把值绑定住了。6 add 加法。去掉wraps后变成w None——元数据丢失,help()、调试器、日志里都只看到w。[('a', 'b', 'c'), ('d', 'e', 'f'), ('g',)](最后一组不足 3 个照样产出);[('a', ['a', 'a']), ('b', ['b', 'b']), ('a', ['a'])]——groupby只合并相邻相同项,所以a出现了两组;想真正分组要先排序。True False False—— dataclass 自动生成按字段比较的__eq__;is比身份;default_factory让每个实例各自新建列表。3 True [2, 1, 3] False—— 实现了__len__+__getitem__,in、reversed、for自动可用;bool()在没有__bool__时回退到__len__。BCA ['D', 'B', 'C', 'A', 'object']—— MRO 是 D→B→C→A;super()沿 MRO 找下一个,所以 B 的super()是 C 而不是 A。- 先打印
finally,再打印try——finally在return之后、真正返回之前执行。 ['enter', 'val', 'exit']——yield之前是__enter__,yield的值绑定到as v,yield之后(finally)是__exit__。
二、找 bug¶
- 可变默认参数:
tags=[]只在定义时创建一次,多次调用共用同一个列表。改为tags=None,函数内if tags is None: tags = []。 - 定义了
__eq__却没定义__hash__,Python 会把__hash__设为None,实例不可哈希,{Money(1)}抛TypeError。加def __hash__(self): return hash(self.v)(或用@dataclass(frozen=True))。 - 少了
@functools.wraps(fn):被装饰函数的__name__/__doc__/__module__/__wrapped__全部变成wrapper的,help()显示错误的名字与空文档,堆栈里也只看到wrapper。 groupby之前没排序:["apple", "banana", "avocado"]按首字母相邻分组得到a→[apple]、b→[banana]、a→[avocado],字典推导式后一个a覆盖前一个,结果{"a": ["avocado"], "b": ["banana"]}。先sorted(data, key=lambda s: s[0])再groupby。- 吞掉一切异常:拼写错误(
NameError)、bug(TypeError)全部消失,程序带着错误状态继续跑。只捕获能处理的具体异常,至少logger.exception(...)记录后再决定是否继续。
三、写函数(参考实现)¶
16.
from collections.abc import Iterable, Iterator
from itertools import islice
def evens(limit: int) -> Iterator[int]:
n = 0
while n < limit:
yield n
n += 2
def take[T](n: int, it: Iterable[T]) -> list[T]:
return list(islice(it, n))
import functools
from collections.abc import Callable
def retry[**P, R](times: int) -> Callable[[Callable[P, R]], Callable[P, R]]:
def decorator(fn: Callable[P, R]) -> Callable[P, R]:
@functools.wraps(fn)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
for attempt in range(1, times + 1):
try:
return fn(*args, **kwargs)
except Exception:
if attempt == times:
raise
raise AssertionError("unreachable")
return wrapper
return decorator
import os
from collections.abc import Iterator
from contextlib import contextmanager
from pathlib import Path
@contextmanager
def cd(path: Path) -> Iterator[Path]:
old = Path.cwd()
os.chdir(path)
try:
yield Path(path)
finally:
os.chdir(old)
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class Version:
major: int
minor: int
def __str__(self) -> str:
return f"{self.major}.{self.minor}"
frozen=True:① 实例属性不可赋值(赋值抛 FrozenInstanceError);② 自动生成 __hash__,实例可作 dict 键 / 放进 set。order=True 让 Version(1, 2) < Version(1, 10) 按字段顺序比较。
20.
import math
from abc import ABC, abstractmethod
from collections.abc import Iterable
from typing import Protocol
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
class Square(Shape):
def __init__(self, side: float) -> None:
self.side = side
def area(self) -> float:
return self.side ** 2
class HasArea(Protocol):
def area(self) -> float: ...
class Circle: # 不继承 Shape
def __init__(self, r: float) -> None:
self.r = r
def area(self) -> float:
return math.pi * self.r ** 2
def total_area(items: Iterable[HasArea]) -> float:
return sum(item.area() for item in items)
total_area([Square(2), Circle(1)])