7.2 继承与特殊方法
前置:7.1 · 约 35 分钟 · 官方对应:继承、数据模型:特殊方法名称
目标
- 读懂继承、方法重写与
super()。 - 认识常用特殊方法(dunder):
__repr__、__str__、__eq__、__len__、__iter__、__getitem__、__call__、__bool__。 - 读懂
@property、@classmethod、@staticmethod。
核心概念
继承与 super()
Python · 可运行
python
class Block:
def __init__(self, type):
self.type = type
def describe(self):
return f"<{self.type}>"
class TextBlock(Block): # 括号中是父类
def __init__(self, text):
super().__init__("text") # 调用父类的 __init__
self.text = text
def describe(self): # 重写父类方法
return super().describe() + " " + self.text
class ToolUseBlock(Block):
def __init__(self, id, name, input):
super().__init__("tool_use")
self.id, self.name, self.input = id, name, input
blocks = [TextBlock("你好"), ToolUseBlock("t1", "get_weather", {"city": "上海"})]
for b in blocks:
print(b.describe(), isinstance(b, Block))
print(TextBlock.__mro__) # 方法解析顺序:查找方法时依次查找的类读码技巧:看到 class X(Base):,X 中没定义的方法都去 Base(及其父类)中找,顺序见 X.__mro__。
特殊方法:让对象支持内置语法
特殊方法以双下划线开头和结尾,由 Python 在特定语法下自动调用:
| 语法 | 调用的特殊方法 |
|---|---|
repr(obj)、REPL 回显、容器中打印 | __repr__ |
str(obj)、print(obj)、f-string | __str__(未定义时退回 __repr__) |
a == b | __eq__ |
len(obj) | __len__ |
for x in obj | __iter__ |
obj[key] | __getitem__ |
obj(...) | __call__ |
if obj: | __bool__,没有则看 __len__ |
with obj: | __enter__ / __exit__(6.3) |
Python · 可运行
python
class Conversation:
def __init__(self):
self.messages = []
def add(self, role, content):
self.messages.append({"role": role, "content": content})
return self # 返回 self 以支持链式调用
def __len__(self):
return len(self.messages)
def __iter__(self):
return iter(self.messages)
def __getitem__(self, index):
return self.messages[index]
def __repr__(self):
return f"Conversation({len(self)} 条消息)"
conv = Conversation().add("user", "你好").add("assistant", "你好!")
print(conv, len(conv), bool(conv))
print(conv[-1]["content"])
for m in conv:
print(m["role"])__eq__ 与 __call__
Python · 可运行
python
class Money:
def __init__(self, cents):
self.cents = cents
def __eq__(self, other):
return isinstance(other, Money) and self.cents == other.cents
def __repr__(self):
return f"Money({self.cents})"
print(Money(100) == Money(100), Money(100) is Money(100))
class Multiplier:
def __init__(self, k):
self.k = k
def __call__(self, x): # 实例可以像函数一样调用
return x * self.k
double = Multiplier(2)
print(double(21), callable(double))@property、@classmethod、@staticmethod
Python · 可运行
python
class Message:
def __init__(self, role, content):
self.role = role
self.content = content
@property
def is_user(self): # 像属性一样访问,不加括号
return self.role == "user"
@classmethod
def user(cls, content): # 第一个参数是类本身:常用作“备选构造器”
return cls("user", content)
@staticmethod
def valid_role(role): # 不需要实例或类,只是放在类的命名空间里
return role in ("user", "assistant")
m = Message.user("你好")
print(m.is_user, m.role, Message.valid_role("system"))易错点
- 只定义
__eq__:实例会变为不可哈希(__hash__被设为None),不能放进 set 或作为 dict 键;需要时同时定义__hash__,或用@dataclass(frozen=True)。 - 调用 property 时加括号:
m.is_user()抛TypeError: 'bool' object is not callable。 - 子类
__init__忘记调用super().__init__():父类中设置的属性不存在,之后出现AttributeError。 __repr__返回非字符串:抛TypeError。
Python · 可运行演示报错
python
class Base:
def __init__(self):
self.type = "base"
class Child(Base):
def __init__(self):
self.name = "child" # 没有 super().__init__()
print(Child().type)对照
| 概念 | Python | JS | Java |
|---|---|---|---|
| 继承 | class B(A): | class B extends A | class B extends A |
| 调用父类 | super().f() | super.f() | super.f() |
| 多继承 | 支持(MRO) | 不支持(可 mixin) | 仅接口 |
| 字符串表示 | __repr__ / __str__ | toString() | toString() |
| 相等 | __eq__ | 无(只有引用比较) | equals() |
| 运算符重载 | 特殊方法 | 不支持 | 不支持 |
| getter | @property | get x() {} | getX() |
读代码
SDK 中的内容块通常是一组继承同一基类的模型类(示意,名称与 anthropic.types 一致):
python
class TextBlock(BaseModel):
type: Literal["text"]
text: str
class ToolUseBlock(BaseModel):
type: Literal["tool_use"]
id: str
name: str
input: dict[str, object]所以 minimal_agent.py 中:
b.type == "text"用来区分块的种类,再安全地访问只属于该种类的属性(b.text或block.name/block.input/block.id)。print(resp)能显示清晰的内容,是因为模型基类实现了__repr__。- 自己写代码时,用
isinstance(b, ToolUseBlock)或检查b.type都可以;SDK 示例普遍用type字段。
练习
练习 1:块的继承体系
实现 Block 基类(__init__(self, type) 保存 type;__repr__ 返回 f"{类名}(type={self.type!r})",类名用 type(self).__name__ 获取)和子类 TextBlock(text):调用 super().__init__("text") 并保存 text。
练习
class Block:
def __init__(self, type):
self.type = type
class TextBlock(Block):
def __init__(self, text):
self.text = text
快捷键:⌘/Ctrl + Enter 提交检查
练习 2:让对象支持 len、索引与比较
实现 ToolRegistry:register(self, name, func) 注册工具;支持 len(reg)(工具数)、reg["name"](取函数,不存在抛 KeyError)、"name" in reg(实现 __contains__)。
练习
class ToolRegistry:
def __init__(self):
self._tools = {}
def register(self, name, func):
self._tools[name] = func
快捷键:⌘/Ctrl + Enter 提交检查
小结
- 子类继承父类的方法,可重写;用
super()调用父类实现,查找顺序看__mro__。 - 特殊方法让对象支持内置语法:
len()、[]、for、==、()、print。 @property像属性一样访问;@classmethod常作备选构造器。