PY期末
代码题
第二类“代码分析题”不要按概念题背定义,而要按读代码模板准备。你上次看不懂题,很大一部分是因为中文术语没有翻译成代码动作。所以现在重点不是“Python 会不会”,而是:
看到“实例属性、类属性、私有成员、类方法、静态方法、继承、重载、生成器、上下文管理、断言、异常处理”等术语时,能立刻知道它对应哪种代码写法、哪种错误、哪种输出。
根据你这份资料,第二类题最高优先级是:
- OOP 代码分析:
class_instance.py、class_inherit.py、ABC.py、MyArray.py - 函数代码分析:可变参数、默认参数、返回值、作用域、lambda、yield
- 容器/字符串代码分析:列表切片、sort/sorted、字典 get/pop、字符串方法、正则
- 文件与异常:
with open()、编码转换、try/except/finally、assert
一、代码分析题应该怎么做
拿到一段代码,固定按这个顺序看。
1. 先判断:代码能不能运行
优先找这些错误:
| 错误类型 | 常见触发 |
|---|---|
SyntaxError |
语法错,括号/冒号/缩进/参数顺序错 |
NameError |
变量名没定义 |
AttributeError |
对象没有这个属性/方法 |
TypeError |
参数个数错、类型不支持、类方法调用方式错 |
IndexError |
列表下标越界 |
KeyError |
字典键不存在 |
ValueError |
类型转换失败,如 int('abc') |
FileNotFoundError |
读文件但文件不存在 |
UnicodeEncodeError/DecodeError |
编码/解码方式不匹配 |
你的资料里非常容易考 AttributeError 和 TypeError,尤其是 OOP。
2. 再判断:程序执行顺序
重点看:
if __name__ == "__main__":
...
如果脚本直接运行,__name__ == "__main__" 成立;如果被其他文件 import,__name__ 是模块名。
所以代码分析题可能问:
为什么导入模块时某些代码没有执行?
答案就是:被 if __name__ == "__main__": 控制住了。
3. 再判断:变量到底指向谁
Python 变量本质是“名字绑定对象”。
重点区分:
x = [1, 2, 3]
y = x
y.append(4)
print(x)
输出:
[1, 2, 3, 4]
因为 x 和 y 指向同一个列表对象。
但:
x = [1, 2, 3]
y = x.copy()
y.append(4)
print(x)
输出:
[1, 2, 3]
因为 copy() 产生了新列表。
第二类题很喜欢考这种“是不是原地修改”。
4. OOP 代码按“属性查找顺序”分析
这是最重要的。
对象访问属性时,大致按:
先找对象自己的实例属性
找不到,再找类属性
再找父类属性
还找不到,AttributeError
例如你资料里的 class_instance.py:
class DemoClass:
class_attr = "Original Class Attribute"
def __init__(self, value):
self.inst_attr = value
这里:
DemoClass.class_attr
可以,因为 class_attr 是类属性。
但:
DemoClass.inst_attr
会报错:
AttributeError
因为 inst_attr 是在 __init__() 里通过 self.inst_attr 创建的,它属于具体实例,不属于类本身。
正确访问:
obj = DemoClass("Original Instance Attribute")
print(obj.inst_attr)
二、必须掌握的中文术语翻译
你上次机试看不懂题,主要就是这些术语。
1. OOP 高频术语
| 中文术语 | 代码含义 | 看到题目时应该想到 |
|---|---|---|
| 类 | class A: |
模板,不是具体对象 |
| 对象/实例 | a = A() |
由类创建出来的具体东西 |
| 实例化 | A() |
调用类,创建对象 |
| 实例属性 | self.x = ... |
每个对象自己有一份 |
| 类属性 | 写在类内部、方法外部 | 类共享,可以 类名.属性 访问 |
| 成员 | 属性 + 方法 | 类里面定义的东西 |
| 数据成员/成员属性 | 变量形式的成员 | self.name、Class.price |
| 成员方法 | 函数形式的成员 | def show(self): |
| 公有成员 | 普通名字,如 name |
类内类外都能正常访问 |
| 私有成员 | __name |
类外不能直接 obj.__name |
| 受保护成员 | _name |
约定不直接访问,但 Python 不强制 |
| 特殊成员 | __xxx__ |
系统约定,如 __init__ |
| 实例方法 | def f(self): |
通过对象调用,自动传 self |
| 静态方法 | @staticmethod |
不需要 self/cls |
| 类方法 | @classmethod |
第一个参数通常是 cls |
| 构造方法 | __init__() |
创建对象时自动调用 |
| 析构方法 | __del__() |
对象释放时调用 |
| 继承 | class B(A): |
B 复用 A 的属性和方法 |
| *类/父类/超类 | 被继承的类 | A |
| 子类/派生类 | 继承别人的类 | B |
| 方法覆盖/重写 | 子类重新定义父类同名方法 | 多态常靠它实现 |
| 多态 | 同一个方法名,不同对象表现不同 | item.show() 输出不同 |
| 运算符重载 | 重写特殊方法 | __add__ 对应 + |
| 抽象超类/抽象*类 | ABC |
不能直接实例化,子类必须实现抽象方法 |
2. 私有成员是重点
资料里的说法是:
私有成员名以两个下划线
__开头,类外不能直接访问,但可以通过特殊方式访问。
例如:
class A:
def __init__(self):
self.__value = 3
a = A()
print(a.__value)
报错:
AttributeError
但可以:
print(a._A__value)
因为 Python 会把:
__value
改名为:
_A__value
所以题目说:
在类外通过对象
a访问类A的私有成员__value
你就写:
a._A__value
如果题目说“设置为 3”:
a._A__value = 3
3. 类属性 vs 实例属性
这是最容易出代码分析题的地方。
class Car:
price = 100000
def __init__(self, color):
self.color = color
car1 = Car("Red")
car2 = Car("Blue")
这里:
Car.price
car1.price
car2.price
都能访问,因为 price 是类属性。
但:
Car.color
报错,因为 color 是实例属性。
如果:
car1.price = 200000
这不是修改类属性,而是给 car1 新增了一个实例属性 price,遮蔽了类属性。
之后:
print(car1.price) # 200000
print(car2.price) # 100000
print(Car.price) # 100000
4. 实例方法、类方法、静态方法
实例方法
class A:
def show(self):
print("show")
a = A()
a.show()
对象调用时,自动把 a 传给 self。
也可以:
A.show(a)
但不能:
A.show()
因为缺少 self,会报 TypeError。
静态方法
class A:
@staticmethod
def show():
print("static")
可以:
A.show()
a = A()
a.show()
不需要 self。
类方法
class A:
value = 10
@classmethod
def show(cls):
print(cls.value)
可以:
A.show()
a = A()
a.show()
第一个参数 cls 自动接收“类对象”。
三、根据资料,第二类代码题最可能怎么考
1. class_instance.py:类属性/实例属性/方法调用
可能考法:
print(DemoClass.inst_attr)
问:是否报错?为什么?
答案:
报 AttributeError。inst_attr 是在 __init__() 中通过 self.inst_attr 定义的实例属性,只属于具体对象 obj,不属于类 DemoClass,不能通过 DemoClass.inst_attr 访问。
正确写法:
obj = DemoClass("value")
print(obj.inst_attr)
2. ABC.py:抽象*类
资料里有:
class Shape(ABC):
@abstractmethod
def get_area(self):
pass
可能考法:
shape = Shape()
问:为什么错?
答案:
Shape 是抽象超类/抽象*类,含有抽象方法,不能直接实例化。
又如:
class BadRectangle(Shape):
def get_area(self):
return self.width * self.height
如果没有实现 get_perimeter(),也不能实例化。
正确子类必须实现所有抽象方法:
class Circle(Shape):
def get_area(self):
...
def get_perimeter(self):
...
3. class_inherit.py:继承和 super()
资料里有两种调用父类构造方法:
Person.__init__(self, name)
和:
super().__init__(name)
如果题目问区别,可以答:
显式调用父类方法时必须写父类名,并手动传入 self;super() 会自动按照继承关系查找父类方法,不需要手动传 self,代码更简洁,耦合度更低。
代码修改题可能要求:
把显式父类调用改成
super()。
写法:
class Employee(Person):
def __init__(self, name, staff_id):
super().__init__(name)
self.staff_id = staff_id
4. MyArray.py:特殊方法/运算符重载
这是 OOP 里最可能出“代码输出分析”的文件。
例如:
x = MyArray(1, 2, 3)
print(x + 5)
会调用:
__add__(self, n)
输出类似:
[6, 7, 8]
因为 __add__ 返回一个新的 MyArray 对象,而 print() 会调用 __str__()。
常见对应关系要背熟:
| 表达式 | 调用方法 |
|---|---|
x + y |
x.__add__(y) |
x - y |
x.__sub__(y) |
x * y |
x.__mul__(y) |
x / y |
x.__truediv__(y) |
x // y |
x.__floordiv__(y) |
x % y |
x.__mod__(y) |
x ** y |
x.__pow__(y) |
len(x) |
x.__len__() |
x[i] |
x.__getitem__(i) |
x[i] = v |
x.__setitem__(i, v) |
v in x |
x.__contains__(v) |
print(x) |
x.__str__() |
交互式直接看 x |
x.__repr__() |
5. text_file_format.py:编码和文件
这个文件里容易考:
with open(file_name, 'r', encoding='gbk') as f:
read_content = f.read()
题目如果说:
读入 GBK 文件,转存为 UTF-8 文件
模板就是:
with open(old_file, 'r', encoding='gbk') as f:
content = f.read()
with open(new_file, 'w', encoding='utf8') as f:
f.write(content)
如果编码错,可能报:
UnicodeDecodeError
UnicodeEncodeError
资料里还有:
s = '***,陶喆'
s.encode('GB2312')
这可能报错,因为 GB2312 不支持部分汉字,换成 GBK 更稳。
四、函数类代码题怎么准备
1. 可变参数
题目说:
函数可以接收任意多个整数
立刻写:
def max_sum(*nums):
print(max(nums))
print(sum(nums))
*nums 会把多个位置参数收集成元组。
2. 字典参数
题目说:
接收任意多个关键字参数
写:
def f(**kwargs):
print(kwargs)
**kwargs 会把关键字参数收集成字典。
3. 默认值参数
危险点:
def f(x, lst=[]):
lst.append(x)
return lst
这个 lst 只在函数定义时创建一次,所以多次调用会共享同一个列表。
正确写法:
def f(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
4. 返回值
题目问“输出什么”时注意:
def f():
print(1)
x = f()
print(x)
输出:
1
None
因为函数没有 return,默认返回 None。
5. 生成器
题目说:
用生成器方式产生数据
立刻想到:
def gen():
yield ...
例如资料里的质数生成器:
def prime_generator(start, end):
for num in range(start, end + 1):
if is_prime(num):
yield num
含有 yield 的函数不会一次性返回列表,而是创建生成器对象,按需产生数据。
五、代码题备考顺序
第一轮:只练老师代码里的“会报错点”
优先看这些文件:
class_instance.pyABC.pyclass_inherit.pyMyArray.pytext_file_format.py- 实验二 5 个程序
每个文件按下面 4 个问题练:
1. 这段代码有没有错误?
2. 如果有,错误类型是什么?
3. 为什么错?
4. 怎么改?
第二轮:把每个程序改成题目需求
比如实验二:
| 原程序 | 可能改法 |
|---|---|
| 字符统计 | 改成统计空格、标点、汉字 |
| 最大值求和 | 改成返回最大值和平均值 |
| 模拟 sorted | 增加 reverse=True |
| 通讯录 | 增加修改联系人、模糊查询 |
| 质数生成器 | 改成生成质数列表、统计数量、求和 |
OOP:
| 原程序 | 可能改法 |
|---|---|
class_instance.py |
判断类属性/实例属性输出 |
class_inherit.py |
把显式父类调用改成 super() |
ABC.py |
增加矩形类、三角形类 |
MyArray.py |
增加 __gt__、__contains__、__getitem__ 功能 |
| 三维向量类 | 实现 + - * / 运算符重载 |
六、你现在最该背的“代码模板”
1. 类模板
class Student:
school = "XDU" # 类属性
def __init__(self, name, age):
self.name = name # 实例属性
self.age = age
def show(self): # 实例方法
print(self.name, self.age)
2. 继承模板
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
def __init__(self, name, sid):
super().__init__(name)
self.sid = sid
3. 私有成员模板
class A:
def __init__(self):
self.__value = 0
def get_value(self):
return self.__value
类外特殊访问:
a = A()
print(a._A__value)
4. 抽象类模板
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, r):
self.r = r
def area(self):
return 3.14 * self.r * self.r
5. 运算符重载模板
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y, self.z - other.z)
def __mul__(self, n):
return Vector(self.x * n, self.y * n, self.z * n)
def __truediv__(self, n):
return Vector(self.x / n, self.y / n, self.z / n)
def __str__(self):
return f"({self.x}, {self.y}, {self.z})"
这个和第 6-8 章作业里的“三维向量类”高度对应。
6. 文件读写模板
with open("a.txt", "r", encoding="utf8") as f:
content = f.read()
with open("b.txt", "w", encoding="utf8") as f:
f.write(content)
7. 异常处理模板
try:
x = int(input())
print(10 / x)
except ValueError:
print("输入必须是整数")
except ZeroDivisionError:
print("除数不能为0")
else:
print("没有异常")
finally:
print("无论是否异常都会执行")
七、考试答题格式
代码分析题不要只写“报错”或“输出”。最好按这个格式:
该代码会报 AttributeError。
原因是 inst_attr 是实例属性,只在对象创建时通过 self.inst_attr 定义,
不能通过类名 DemoClass.inst_attr 访问。
应先创建对象 obj = DemoClass(...),再使用 obj.inst_attr 访问。
输出题按这个格式:
输出为:
...
原因:
1. ...
2. ...
3. ...
修改题按这个格式:
错误位置:第 x 行 ...
错误原因:...
修改后代码:
...
修改后作用:...
八、最短备考方案
现在别平均用力。按这个顺序:
必做
- 把 OOP 术语表背熟。
- 手敲一遍三维向量类。
- 看懂
class_instance.py每一行为什么能/不能访问。 - 看懂
ABC.py为什么Shape()和BadRectangle()会报错。 - 看懂
MyArray.py里+、//、in、len、[]、print()分别调用哪个特殊方法。 - 把实验二 5 个程序能默写出核心函数。
次做
- 文件 GBK 转 UTF-8。
try-except-else-finally。- 字典通讯录增删查改。
- 正则
re.findall()、re.sub()。 yield生成器。
核心判断:这门代码分析题不是考复杂算法,主要考 Python 语义和老师 PPT 术语。你现在最该补的是“中文术语 → 代码行为 → 常见报错”。

浙公网安备 33010602011771号