Subclassing Built-In Types Is Tricky
CPython 中用 C 写的内置类型,如 list
dict
str
,在继承行为上跟纯 Python 类不一致。比如:
>>> class DoppelDict(dict):
... def __setitem__(self, key, value):
... super().__setitem__(key, [value] * 2)
...
# __init__ 函数无视了子类定义的 __setitem__,这是不合理的
>>> dd = DoppelDict(one=1)
>>> dd
{'one': 1}
# 这个是合预期的
>>> dd['two'] = 2
>>> dd
{'one': 1, 'two': [2, 2]}
# update 函数又无视了子类的 __setitem__
>>> dd.update(three=3)
>>> dd
{'three': 3, 'one': 1, 'two': [2, 2]}
如果你想要弄一个 custom dict,用 collections.UserDict
。同理还有 UserList
UserStr
。
Soapbox 中的「Misbehaving Built-ins: Bug or Feature?」一节,提到了 built-in 类型行为不一致的问题。作者觉得是基于性能和扩展性考虑的折衷行为。毕竟你很少需要 subclass 一个 dict,但是你几乎每天都在用 dict 本身。所以性能上的考虑优于扩展性。
Multiple Inheritance and Method Resolution Order
这是 tkinter
库中的 Text
类的 MRO 解析顺序图。图中应该从右到左看,比如 Widget
是 Text
的第一个父类,XView
是第二个。代码中 Text
的继承顺序如下:
class Text(Widget, XView, YView):
...
Coping with multiple inheritance
这是本章的重点。我觉得写得非常好。
1. Distinguish Interface Inheritance from Implementation Inheritance
When dealing with multiple inheritance, it’s useful to keep straight the reasons why subclassing is done in the first place. The main reasons are:
- Inheritance of interface creates a subtype, implying an “is-a” relationship.
- Inheritance of implementation avoids code duplication by reuse.
Interface inheritance is the backbone of a framework.
2. Make Interfaces Explicit with ABCs
3. Use Mixins for Code Reuse
If a class is designed to provide method implementations for reuse by multiple unrelated subclasses, without implying an “is-a” relationship, it should be an explicit mixin class.
Conceptually, a mixin does not define a new type; it merely bundles methods for reuse. A mixin should never be instantiated, and concrete classes should not inherit only from a mixin. Each mixin should provide a single specific behavior, implementing few and very closely related methods.
4. Make Mixins Explicit by Naming
5. An ABC May Also Be a Mixin; The Reverse Is Not True
6. Don't Subclass from More Than One Concrete Class
Scott Meyer 在 Effective C++ 有更激进的说法:"all non-leaf classes should be abstract"。
7. Provide Aggregate Classes to Users
这一点,Django REST Framework 的 ModelViewSet
(代码)让我印象深刻。
8. “Favor Object Composition Over Class Inheritance.”
如 UserDict
就是一个好的例子。
A Modern Example: Mixins in Django Generic Views
Django 的各种 class-based view,是充分利用了 Python 的继承和 ABC 能力的典型例子。DRF 也是。比如 TemplateView
继承了:
TemplateResponseMixin
:提供了渲染模板成HttpResponse
的能力ContextMixin
:提供了获得 context 的能力View
:提供了通用的 View 函数能力(虽然你不需要用它)
Further Reading
如果有时间,仔细看看这里面推荐的内容。
内容:
- 关于
super
的争论 - 怎样用好多重继承
- 面向对象设计的好书推荐:Grady Booch's Object Oriented Analysis and Design
Soapbox 讨论了各语言对类继承做的选择。