跳转至

第 1 周周测答案(默认折叠)

闭卷做完、自己判完再看

点击展开

第 1 周周测答案

所有输出已在 Python 3.14.4 上实际运行核对。

一、预测输出

  1. 14 0 —— 生成器只能遍历一次,第二次 sum 时已经耗尽。
  2. 1 [2]"done" 成了 StopIteration.valuelist() 会静默吞掉它;只有 yield from gen() 这样的委托才能把它当作表达式的值拿到。
  3. [2, 2, 2] [0, 1, 2] —— 闭包晚绑定:lambda: i 在调用时才查 i,此时循环已结束、i == 2;默认参数 i=i 在定义时就把值绑定住了。
  4. 6 add 加法。去掉 wraps 后变成 w None——元数据丢失,help()、调试器、日志里都只看到 w
  5. [('a', 'b', 'c'), ('d', 'e', 'f'), ('g',)](最后一组不足 3 个照样产出);[('a', ['a', 'a']), ('b', ['b', 'b']), ('a', ['a'])]——groupby 只合并相邻相同项,所以 a 出现了两组;想真正分组要先排序。
  6. True False False —— dataclass 自动生成按字段比较的 __eq__is 比身份;default_factory 让每个实例各自新建列表。
  7. 3 True [2, 1, 3] False —— 实现了 __len__ + __getitem__inreversedfor 自动可用;bool() 在没有 __bool__ 时回退到 __len__
  8. BCA ['D', 'B', 'C', 'A', 'object'] —— MRO 是 D→B→C→A;super() 沿 MRO 找下一个,所以 B 的 super() 是 C 而不是 A。
  9. 先打印 finally,再打印 try —— finallyreturn 之后、真正返回之前执行。
  10. ['enter', 'val', 'exit'] —— yield 之前是 __enter__yield 的值绑定到 as vyield 之后(finally)是 __exit__

二、找 bug

  1. 可变默认参数:tags=[] 只在定义时创建一次,多次调用共用同一个列表。改为 tags=None,函数内 if tags is None: tags = []
  2. 定义了 __eq__ 却没定义 __hash__,Python 会把 __hash__ 设为 None,实例不可哈希,{Money(1)}TypeError。加 def __hash__(self): return hash(self.v)(或用 @dataclass(frozen=True))。
  3. 少了 @functools.wraps(fn):被装饰函数的 __name__/__doc__/__module__/__wrapped__ 全部变成 wrapper 的,help() 显示错误的名字与空文档,堆栈里也只看到 wrapper
  4. groupby 之前没排序:["apple", "banana", "avocado"] 按首字母相邻分组得到 a→[apple]b→[banana]a→[avocado],字典推导式后一个 a 覆盖前一个,结果 {"a": ["avocado"], "b": ["banana"]}。先 sorted(data, key=lambda s: s[0])groupby
  5. 吞掉一切异常:拼写错误(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))
17.
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
18.
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)
19.
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=TrueVersion(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)])
区别:ABC 是名义子类型——必须显式继承并实现抽象方法,否则实例化报错;Protocol 是结构子类型——只要长得像(有同名同签名的方法)就算实现,不需要继承,类型检查器据此检查。