Python核心知识点自测:你能答对几个?

作者:沙与沫2026.11.07 01:44浏览量:0

简介:很多Python学习者“看得懂却写不出”,症结在于对核心机制理解不透。本文按数据结构、函数机制、面向对象、迭代器与生成器、上下文管理、异常处理及标准库六大模块整理自测考点,每组均附可运行代码与输出解析。建议先预测结果再运行验证,用错题定位薄弱环节,夯实从基础到进阶的完整知识体系。

很多 Python 学习者都有类似体会:教程看得明白,代码也能读顺,可一旦合上资料独立编写,或面对一段“看似简单”的代码判断输出结果,就频频出错。这说明“看懂”和“掌握”之间存在明显断层。本文按难度递进整理了六大模块的自测点,每个知识点均配有可直接运行的代码示例。建议你先遮住注释中的输出结果,独立预测,再动手运行验证——预测与实际不符的地方,就是知识盲区所在。

一、第一关:数据结构与对象模型

1.1 引用赋值:is 与 == 的区别

Python 中变量是对象的“标签”,赋值传递的是引用而非副本。判断两个变量是否指向同一个对象要用 is,判断值是否相等用 ==。

  1. a = [1, 2, 3]
  2. b = a # b 和 a 指向同一个列表
  3. b.append(4)
  4. print(a) # [1, 2, 3, 4],修改 b 等于修改 a
  5. x = [1, 2, 3]
  6. y = [1, 2, 3]
  7. print(x == y) # True,内容相同
  8. print(x is y) # False,两个独立对象

自测要点:日常开发中应默认使用 == 做值比较,仅在与 None、True、False 等单例比较时使用 is。如果你曾用 is 比较两个字符串或列表,说明这一关没过。

1.2 浅拷贝与深拷贝

嵌套结构中,浅拷贝只复制最外层容器,内层子对象仍然共享;深拷贝才会递归复制所有层级。

  1. import copy
  2. src = [[1, 2], [3, 4]]
  3. shallow = src.copy() # 浅拷贝
  4. deep = copy.deepcopy(src) # 深拷贝
  5. src[0].append(99)
  6. print(shallow[0]) # [1, 2, 99],内层列表仍被共享
  7. print(deep[0]) # [1, 2],完全独立

自测要点:能说出 list.copy()、切片 [:]、dict(dict) 等操作都属于浅拷贝,并知道何时必须使用 deepcopy。

1.3 切片与推导式

切片语法 [start:stop:step] 含头不含尾;推导式是从“循环 + append”到声明式表达的升级。

  1. nums = list(range(10))
  2. print(nums[2:8:2]) # [2, 4, 6]
  3. print(nums[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
  4. matrix = [[1, 2, 3], [4, 5, 6]]
  5. flat = [x for row in matrix for x in row]
  6. print(flat) # [1, 2, 3, 4, 5, 6]

1.4 字典与集合

字典取值推荐用 get 避免 KeyError;集合天然适合去重与交并差运算。

  1. scores = {'alice': 90, 'bob': 85}
  2. print(scores.get('carol', 0)) # 0,键不存在时返回默认值
  3. squares = {n: n ** 2 for n in range(4)}
  4. print(squares) # {0: 0, 1: 1, 2: 4, 3: 9}
  5. a, b = {1, 2, 3}, {2, 3, 4}
  6. print(a & b) # {2, 3} 交集
  7. print(a | b) # {1, 2, 3, 4} 并集
  8. print(a - b) # {1} 差集

二、第二关:函数机制

2.1 可变默认参数陷阱(高频考点)

默认参数在函数定义时求值一次,之后所有调用共享同一个对象,这是最经典的“隐式状态共享”陷阱。

  1. def append_to(item, target=[]):
  2. target.append(item)
  3. return target
  4. print(append_to(1)) # [1]
  5. print(append_to(2)) # [1, 2],而很多人预期是 [2]
  6. # 正确写法:用 None 作哨兵值
  7. def append_to_safe(item, target=None):
  8. if target is None:
  9. target = []
  10. target.append(item)
  11. return target
  12. print(append_to_safe(1)) # [1]
  13. print(append_to_safe(2)) # [2]

2.2 闭包的延迟绑定

闭包捕获的是变量本身而非创建那一刻的值,循环结束后再调用,读到的是循环变量的最终值。

  1. funcs = [lambda: i for i in range(3)]
  2. print([f() for f in funcs]) # [2, 2, 2],而非 [0, 1, 2]
  3. # 修复:利用默认参数在定义时求值的特性固化当前值
  4. funcs = [lambda i=i: i for i in range(3)]
  5. print([f() for f in funcs]) # [0, 1, 2]

2.3 args 与 *kwargs

  1. def log_call(level, *args, **kwargs):
  2. print(f"[{level}] args={args} kwargs={kwargs}")
  3. log_call("INFO", "login", "success", user_id=42)
  4. # [INFO] args=('login', 'success') kwargs={'user_id': 42}

2.4 装饰器:函数复用的利器

装饰器本质是“接收函数并返回新函数”的高阶函数。@functools.wraps 用于把原函数的 name、doc 等元信息复制到包装函数上,避免函数被装饰后“改名”,破坏文档与内省。

  1. import functools
  2. import time
  3. def timed(func):
  4. @functools.wraps(func)
  5. def wrapper(*args, **kwargs):
  6. start = time.perf_counter()
  7. result = func(*args, **kwargs)
  8. cost = time.perf_counter() - start
  9. print(f"{func.__name__} 耗时 {cost:.4f}s")
  10. return result
  11. return wrapper
  12. @timed
  13. def slow_sum(n):
  14. return sum(range(n))
  15. print(slow_sum(10_000_000))
  16. # 先打印类似:slow_sum 耗时 0.2xxxs,随后输出求和结果

自测要点:不借助资料能写出一个计时装饰器并解释 wraps 的作用才算过关;能进一步实现“带参数的装饰器”(三层嵌套结构)属于加分项。

三、第三关:面向对象

3.1 类属性与实例属性

实例查找属性时先查自身,再沿继承链查类;对实例赋值只会“遮蔽”类属性,不会修改它。

  1. class Dog:
  2. species = "犬科" # 类属性,所有实例共享
  3. def __init__(self, name):
  4. self.name = name # 实例属性
  5. d1, d2 = Dog("旺财"), Dog("小黑")
  6. d1.species = "猫科" # 只在 d1 上创建同名实例属性
  7. print(d2.species) # 犬科
  8. Dog.species = "哺乳类" # 通过类名修改类属性
  9. print(d2.species) # 哺乳类
  10. print(d1.species) # 猫科,实例属性优先级更高

3.2 魔术方法:让对象支持内置运算

  1. class Vector:
  2. def __init__(self, x, y):
  3. self.x, self.y = x, y
  4. def __add__(self, other):
  5. return Vector(self.x + other.x, self.y + other.y)
  6. def __eq__(self, other):
  7. return (self.x, self.y) == (other.x, other.y)
  8. def __repr__(self):
  9. return f"Vector({self.x}, {self.y})"
  10. print(Vector(1, 2) + Vector(3, 4)) # Vector(4, 6)
  11. print(Vector(1, 2) == Vector(1, 2)) # True

自测要点:至少掌握 init、repr、str、len、eq、getitem 等常见魔术方法的触发时机。

3.3 property:把方法伪装成属性并加校验

  1. class Account:
  2. def __init__(self, balance=0):
  3. self._balance = balance
  4. @property
  5. def balance(self):
  6. return self._balance
  7. @balance.setter
  8. def balance(self, value):
  9. if value < 0:
  10. raise ValueError("余额不能为负")
  11. self._balance = value
  12. acc = Account(100)
  13. acc.balance = 150 # 像属性一样赋值,实则触发 setter 校验
  14. print(acc.balance) # 150

3.4 继承与 super

  1. class Animal:
  2. def __init__(self, name):
  3. self.name = name
  4. def speak(self):
  5. raise NotImplementedError("子类必须实现 speak")
  6. class Cat(Animal):
  7. def speak(self):
  8. return f"{self.name}:喵"
  9. class DogChild(Animal):
  10. def __init__(self, name, breed):
  11. super().__init__(name) # 复用父类初始化,避免重复代码
  12. self.breed = breed
  13. def speak(self):
  14. return f"{self.name}:汪"
  15. print(Cat("咪咪").speak()) # 咪咪:喵
  16. print(DogChild("大黄", "柴犬").speak()) # 大黄:汪

四、第四关:迭代器与生成器

4.1 手写迭代器,理解 for 循环的本质

for 循环会先调用 iter() 获取迭代器,再不断调用 next(),直到抛出 StopIteration。

  1. class Countdown:
  2. def __init__(self, start):
  3. self.current = start
  4. def __iter__(self):
  5. return self
  6. def __next__(self):
  7. if self.current <= 0:
  8. raise StopIteration
  9. self.current -= 1
  10. return self.current + 1
  11. for n in Countdown(3):
  12. print(n) # 依次输出 3 2 1

4.2 生成器:yield 暂停函数,惰性求值

含 yield 的函数调用后并不立即执行,而是返回生成器对象;每次 next() 执行到 yield 处暂停并交出值,函数的局部状态被完整保留。这让生成器在处理超大数据集时内存占用极低。

  1. def fibonacci():
  2. a, b = 0, 1
  3. while True:
  4. yield a
  5. a, b = b, a + b
  6. fib = fibonacci()
  7. print([next(fib) for _ in range(8)])
  8. # [0, 1, 1, 2, 3, 5, 8, 13]

处理大文件时同理,逐行产出而不是一次性读入:

  1. def read_lines(path):
  2. with open(path, encoding='utf-8') as f:
  3. for line in f:
  4. yield line.strip()

4.3 生成器表达式:省内存的推导式

  1. total = sum(n * n for n in range(1_000_000)) # 不生成百万元素的列表
  2. squares = [n * n for n in range(10)] # 列表推导式则立即分配内存

自测要点:能说清“生成器只能消费一次”——迭代到底后再次迭代将得到空结果,需要重新创建。

五、第五关:上下文管理与异常处理

5.1 with 的本质

with 语句在进入代码块时调用对象的 enter,退出时(无论是否发生异常)调用 exit,从而把“清理资源”从业务代码中剥离。

  1. import time
  2. class Timer:
  3. def __enter__(self):
  4. self.start = time.perf_counter()
  5. return self
  6. def __exit__(self, exc_type, exc_val, exc_tb):
  7. self.cost = time.perf_counter() - self.start
  8. print(f"耗时 {self.cost:.4f}s")
  9. return False # 返回 False 表示不吞掉异常
  10. with Timer():
  11. sum(range(5_000_000))
  12. # 代码块结束时打印耗时,即使块内抛异常也会执行

5.2 用 @contextmanager 快速实现

  1. from contextlib import contextmanager
  2. @contextmanager
  3. def html_tag(tag):
  4. print(f"<{tag}>")
  5. try:
  6. yield
  7. finally:
  8. print(f"</{tag}>")
  9. with html_tag("div"):
  10. print("内容")
  11. # 输出:<div> / 内容 / </div>

5.3 结构化异常处理与 EAFP 风格

Python 社区推崇 EAFP(先执行、出错再兜底)优于 LBYL(先检查再行动)。

  1. config = {"host": "127.0.0.1"}
  2. # LBYL 风格:先检查
  3. port = config["port"] if "port" in config else 8080
  4. # EAFP 风格:先执行,出错再兜底(更 Pythonic)
  5. try:
  6. port = config["port"]
  7. except KeyError:
  8. port = 8080
  9. print(port) # 8080
  10. def parse_int(text):
  11. try:
  12. return int(text)
  13. except ValueError as e:
  14. print(f"无法解析 {text!r}:{e}")
  15. return None
  16. finally:
  17. pass # 无论成败都执行,适合收尾清理
  18. print(parse_int("42")) # 42
  19. print(parse_int("hello")) # 打印提示并返回 None

自测要点:知道裸 except 会连 KeyboardInterrupt 一起吞掉、except 顺序要从具体到宽泛、异常链 from 的用途。

六、第六关:高频标准库

标准库是 Python“自带电池”的核心竞争力,熟练使用可以少写大量样板代码。

  1. from collections import Counter, defaultdict
  2. words = "the quick brown fox jumps over the lazy dog the".split()
  3. print(Counter(words).most_common(2))
  4. # [('the', 3), ('quick', 1)]
  5. groups = defaultdict(list)
  6. for name, dept in [("Alice", "dev"), ("Bob", "ops"), ("Carol", "dev")]:
  7. groups[dept].append(name)
  8. print(dict(groups))
  9. # {'dev': ['Alice', 'Carol'], 'ops': ['Bob']}
  1. from itertools import chain, product
  2. from pathlib import Path
  3. import json
  4. print(list(chain([1, 2], [3, 4]))) # [1, 2, 3, 4]
  5. print(list(product("AB", repeat=2))) # [('A','A'), ('A','B'), ('B','A'), ('B','B')]
  6. json_files = list(Path("./conf").glob("*.json")) # 面向对象方式查找文件
  7. data = {"name": "Alice", "tags": ["python"]}
  8. text = json.dumps(data, ensure_ascii=False) # 序列化为 JSON 字符串
  9. back = json.loads(text) # 反序列化

内置函数 enumerate 与 zip 让循环索引和并行遍历更简洁:

  1. names = ["Alice", "Bob"]
  2. scores = [90, 85]
  3. for rank, (name, score) in enumerate(zip(names, scores), start=1):
  4. print(f"第{rank}名:{name},{score}分")
  5. # 第1名:Alice,90分
  6. # 第2名:Bob,85分

七、评估你的自测结果

以上六大关约二十个考点,可按以下标准给自己定位:

  • 全部能独立预测并解释原理:基础扎实,可以放心进入 Web 开发、数据分析、自动化运维、人工智能等进阶方向;
  • 60% 以上通过:处于“合格但不牢固”区间,建议针对错题重读对应小节,并把示例代码默写一遍;
  • 通过率不足 60%:不必焦虑,这说明当前的学习还停留在“复制粘贴”层,应回到数据结构与函数机制重新打地基,并坚持“每学一个语法点必须亲手运行三个变体”的习惯。

三条通用建议:

  1. 错题优先。预测与实际输出不符的考点,往往对应理解偏差,仅靠重看教程无法纠正,必须通过“运行—修改—再运行”的过程重建认知;
  2. 用小项目串联知识点。例如写一个命令行待办事项工具,会自然覆盖类设计、文件读写、异常处理、参数解析等考点,比零散刷题更容易形成体系;
  3. 阶段性回测。语言特性的遗忘曲线非常明显,每隔一两个月把本文示例重新过一遍,能通过的速度会直观反映你的成长。

Python 的易学性容易让人产生“已经掌握”的错觉,而面试与真实项目只认代码层面的硬功夫。把上面每一道示例都在本地跑一遍、改一遍、再解释一遍,你收获的将不只是知识点本身,而是把知识转化为生产力的能力。