FluentPythonCh12

5th December 2017 at 6:39am
Fluent Python

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 解析顺序图。图中应该从右到左看,比如 WidgetText 的第一个父类,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 讨论了各语言对类继承做的选择。