Interfaces and polymorphism
Introduzido| English | Significado |
|---|---|
| interface/ˈɪntəfeɪs/ | interface |
| polymorphism/ˈpɒlɪmɔːfɪzəm/ | polymorphism |
| duck typing | duck typing |
| runtime type | runtime type |
| dynamic dispatch | dynamic dispatch |
| open/closed | open/closed |
| substitution/ˌsʌbstɪˈtjuːʃn/ | A mutation in which one nucleotide or base pair is replaced by another. |
Email today, SMS tomorrow
- The library sends due-date reminders by email. Next term it adds SMS, and later a message queue for another app.
- If the reminder code names each class, it must change every time. If it names only the behaviour "send", it never changes.
Name the behaviour
- An interface 接口 names what a thing does (
send), not which class it is. - Polymorphism 多态 is one call site running many implementations:
n.send(text)works for every kind of sender.
Duck typing and Protocol
- Python is dynamically typed: any object with the right methods fits. This is duck typing 鸭子类型.
- A
Protocolwrites the contract down without forcing inheritance, so tools can check it.
class Notifier(Protocol):
def send(self, text: str) -> str: ...
A Protocol names —
Dificuldade 1/5 · Moedas 100 + pequeno bônus na primeira conclusão hoje
It is a structural contract: any object with the named methods fits, no inheritance needed.
To be used where a Notifier Protocol is expected, a class must inherit from Notifier.
Dificuldade 1/5 · Moedas 100 + pequeno bônus na primeira conclusão hoje
A Protocol is structural. Any class with a matching send method fits, with no inheritance.
Dispatch follows the object
- When
n.send('loan due')runs, Python looks upsendon the runtime type 运行时类型: the class of the object, not the variable's annotation. - This is dynamic dispatch 动态绑定: the call is written once, and the method is chosen for each object at call time.

A variable holding SmsNotify() runs n.send(due): the call goes to —
Dificuldade 1/5 · Moedas 100 + pequeno bônus na primeira conclusão hoje
Dynamic dispatch resolves on the object the variable holds, not on the annotation.
Match each term to its meaning.
Dificuldade 2/5 · Moedas 140 + pequeno bônus na primeira conclusão hoje
Together they let callers depend on behaviour, not classes.
Put the steps of the call n.send('loan due') in order.
Dificuldade 3/5 · Moedas 200 + pequeno bônus na primeira conclusão hoje
The variable's annotation plays no part; the object decides.
Open to new types
- Adding a fourth sender class touches no existing line: the caller was never told the concrete classes.
- This is the open/closed 开闭 idea: open to new types, closed to editing.
The reminder loop works with three sender classes. A fourth sender class is written. How many lines inside the loop must change?
Dificuldade 2/5 · Moedas 140 + pequeno bônus na primeira conclusão hoje
The loop only calls n.send. The new class is added to the list; the loop itself is closed to edits.
Substitution is a promise
- Substitution 替换 (the Liskov rule): every implementation must accept at least what the interface promises and keep every guarantee. Preconditions may only widen; guarantees may only grow.
- A
SilentNotify.sendthat raises on an empty string narrows the contract. It breaks a caller who was told thatsendnever raises.
A substitute implementation may —
Dificuldade 1/5 · Moedas 100 + pequeno bônus na primeira conclusão hoje
Substitution: accept at least what was promised, break no promise.
Replace isinstance chains
- A chain like
if isinstance(n, EmailNotify): ... elif isinstance(n, SmsNotify): ...must be edited for every new type. - Give each class its own method instead and let dispatch choose. That is what polymorphism is for.
Worked example: one loop, three senders
team = [EmailNotify(), SmsNotify(), QueueNotify()], thenfor n in team: print(n.send('loan due')).- Output:
email: loan due,sms: loan due,queue: loan due. Three classes, one call site, noif.
Three common slips
- Writing
isinstancechains. Each new type means another edit; use a method per class. - Adding a precondition in an override. Callers who trusted the interface break.
- Thinking the annotation picks the method. The object's runtime type does.
Recap
- An interface names behaviour; a Protocol writes it down; duck typing lets any fitting object in.
- Dynamic dispatch picks the method from the runtime type, so new types need no caller edits.
- Substitutes must honour the whole contract: widen inputs, keep every guarantee.