<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 本章将讨论继承和子类化，重点是说明对 Python 而言尤为重要的两个细节：
子类化内置类型的缺点
多重继承的方法和解析顺序
我们将通过两个重要的 Python 项目探讨多重继承，这两个项目是 GUI 工具包 Tkinter 和 Web 框架 Django
我们将首先分析子类化内置类型的问题，然后讨论多重继承，通过案例讨论类层次结构方面好的做法和不好的
子类化内置类型很麻烦
在 Python 2.2 之前内置类型（如 list 和 dict）不能子类化，之后可以了，但是有个重要事项：内置类型（使用 C 语言编写）不会调用用户定义的类覆盖的特殊方法
至于内置类型的子类覆盖的方法会不会隐式调用，CPython 没有官方规定，基本上，内置类型的方法不会调用子类覆盖的方法。例如，dict 的子类覆盖 __getitem__() 方法不会被内置类型的 get() 方法调用，下面说明了这个问题：
内置类型的 dict 的 __init__ 和 __update__ 方法会忽略我们覆盖的 __setitem__ 方法
Step1:  原生类型的这种行为违背了面向对象编程的一个基本原则：始终应该从实例（self）所属的类开始搜索方法，即使在超类实现的类中调用也是如此。在这种糟糕的局面中，__missing__ 却能按照预期工作（3.4 节），但这是特例
不止实例内部有这个问题（self.get() 不调用 self.__getitem__()），内置类型的方法调用其他类的方法，如果被覆盖了，也不会被调用。下面是个例子，改编自 PyPy 文档
dict.update 方法会忽略 AnswerDict.__getitem__ 方法
Step2:  直接子类化内置类型（如 dict，list，str）容易出错，因为内置类型的方法通常忽略用户覆盖的方法，不要子类化内置类型，用户自己定义的类应该继承 collections 模块中的类，例如 UserDict, UserList, UserString，这些类，这些类做了特殊设计，因此易于扩展
如果子类化的是 collections.UserDict，上面暴露的问题就迎刃而解了，如下：
Step3:  综上，本节所述的问题只是针对与 C 语言实现的内置类型内部的方法委托上，而且只影响直接继承内置类型的用户自定义类。如果子类化使用 Python 编写的类，如 UserDict 和 MutableMapping，就不会受此影响
多重继承和方法解析顺序
任何实现多重继承的语言都要处理潜在的命名冲突，这种冲突由不相关的祖先类实现同命方法引起，这种冲突称为菱形问题。
Step4:  B 和 C 都实现了 pong 方法，唯一区别就是打印不一样。在 D 上调用 d.pong 运行的是哪个 pong 方法呢？ C++ 中，必须使用类名限定方法调用来避免歧义。Python 也可以，如下：
Step5:  Python 能区分 d.pong() 调用的是哪个方法，因为 Python 会按照特定的顺序遍历继承图，这个顺序叫顺序解析（Method Resolution Order，MRO）。类都有一个名为 __mro__ 的属性，它的值是一个元组，按照方法解析顺序列出各个超类。从当前类一直向上，直到 object 类。D 类的 __mro__ 属性如下：
Step6:  若想把方法调用委托给超类，推荐的方法是使用内置的 super() 函数。在 Python 3 中，这种方式变得更容易了，如上面的 D 类中的 pingpong 方法所示。然而，有时可能幸亏绕过方法解析顺序，直接调用某个类的超方法 -- 这样有时更加方便。，例如，D.ping 方法可以这样写
Step7:  注意，直接在类上调用实例方法时，必须显式传入 self 参数，因为这样访问的是未绑定方法（unbound method）
然而，使用 super() 最安全，也不易过时，调用框架或不受自己控制的类层次结构中的方法时，尤其适合用 super()。使用 super() 调用方法时，会遵循方法解析顺序，如下所示：
Step8:  下面看看 D 在实例上调用 pingpong 方法得到的结果，如下所示：
Step9:  方法解析顺序不仅考虑继承图，还考虑子类声明中列出超类的顺序。也就是说，如果声明 D 类时把 D 声明为 class D（C, B)，那么 D 类的 __mro__ 就会不一样，先搜索 C 类，再 搜索 B 类
分析类时，我们需要经常查看 __mro__ 属性，下面是一些常用类的方法搜索顺序
Step10:  结束方法解析之前，我们再看看 Tkinter 复杂的多重继承：
<ASSISTANT_TASK:>
Python Code:
class DoppelDict(dict):
    def __setitem__(self, key, value):
        super().__setitem__(key, [value] * 2)
dd = DoppelDict(one=1)
dd # 继承 dict 的 __init__ 方法忽略了我们覆盖的 __setitem__方法，'one' 值没有重复
dd['two'] = 2 # `[]` 运算符会调用我们覆盖的 __setitem__ 方法
dd
dd.update(three=3) #继承自 dict 的 update 方法也不会调用我们覆盖的 __setitem__ 方法
dd
Explanation: 本章将讨论继承和子类化，重点是说明对 Python 而言尤为重要的两个细节：
子类化内置类型的缺点
多重继承的方法和解析顺序
我们将通过两个重要的 Python 项目探讨多重继承，这两个项目是 GUI 工具包 Tkinter 和 Web 框架 Django
我们将首先分析子类化内置类型的问题，然后讨论多重继承，通过案例讨论类层次结构方面好的做法和不好的
子类化内置类型很麻烦
在 Python 2.2 之前内置类型（如 list 和 dict）不能子类化，之后可以了，但是有个重要事项：内置类型（使用 C 语言编写）不会调用用户定义的类覆盖的特殊方法
至于内置类型的子类覆盖的方法会不会隐式调用，CPython 没有官方规定，基本上，内置类型的方法不会调用子类覆盖的方法。例如，dict 的子类覆盖 __getitem__() 方法不会被内置类型的 get() 方法调用，下面说明了这个问题：
内置类型的 dict 的 __init__ 和 __update__ 方法会忽略我们覆盖的 __setitem__ 方法
End of explanation
class AnswerDict(dict):
    def __getitem__(self, key):
        return 42
ad = AnswerDict(a='foo')
ad['a']  # 返回 42，与预期相符
d = {}  
d.update(ad) # d 是 dict 的实例，使用 ad 中的值更新 d
d['a']       #dict.update 方法忽略了 AnswerDict.__getitem__ 方法
Explanation: 原生类型的这种行为违背了面向对象编程的一个基本原则：始终应该从实例（self）所属的类开始搜索方法，即使在超类实现的类中调用也是如此。在这种糟糕的局面中，__missing__ 却能按照预期工作（3.4 节），但这是特例
不止实例内部有这个问题（self.get() 不调用 self.__getitem__()），内置类型的方法调用其他类的方法，如果被覆盖了，也不会被调用。下面是个例子，改编自 PyPy 文档
dict.update 方法会忽略 AnswerDict.__getitem__ 方法
End of explanation
import collections
class DoppelDict2(collections.UserDict):
    def __setitem__(self, key, value):
        super().__setitem__(key, [value] * 2)
dd = DoppelDict2(one=1)
dd
dd['two'] = 2
dd
dd.update(three=3)
dd
class AnswerDict2(collections.UserDict):
    def __getitem__(self, key):
        return 42
ad = AnswerDict2(a='foo')
ad['a']
d = {}
d.update(ad)
d['a']
d
ad  # 这里是自己加的，感觉还是有点问题，但是调用时候结果符合预期
Explanation: 直接子类化内置类型（如 dict，list，str）容易出错，因为内置类型的方法通常忽略用户覆盖的方法，不要子类化内置类型，用户自己定义的类应该继承 collections 模块中的类，例如 UserDict, UserList, UserString，这些类，这些类做了特殊设计，因此易于扩展
如果子类化的是 collections.UserDict，上面暴露的问题就迎刃而解了，如下：
End of explanation
class A:
    def ping(self):
        print('ping', self)
    
class B(A):
    def pong(self):
        print('pong', self)
        
class C(A):
    def pong(self):
        print('PONG', self)
        
class D(B, C):
    
    def ping(self):
        super().ping()
        print('post-ping:', self)
        
    def pingpong(self):
        self.ping()
        super().ping()
        self.pong()
        super().pong
        C.pong(self)
Explanation: 综上，本节所述的问题只是针对与 C 语言实现的内置类型内部的方法委托上，而且只影响直接继承内置类型的用户自定义类。如果子类化使用 Python 编写的类，如 UserDict 和 MutableMapping，就不会受此影响
多重继承和方法解析顺序
任何实现多重继承的语言都要处理潜在的命名冲突，这种冲突由不相关的祖先类实现同命方法引起，这种冲突称为菱形问题。
End of explanation
d = D()
d.pong()  # 直接调用 d.pong() 是调用的 B 类中的版本
C.pong(d) #超类中的方法都可以直接调用，此时要把实例作为显式参数传入
Explanation: B 和 C 都实现了 pong 方法，唯一区别就是打印不一样。在 D 上调用 d.pong 运行的是哪个 pong 方法呢？ C++ 中，必须使用类名限定方法调用来避免歧义。Python 也可以，如下：
End of explanation
D.__mro__
Explanation: Python 能区分 d.pong() 调用的是哪个方法，因为 Python 会按照特定的顺序遍历继承图，这个顺序叫顺序解析（Method Resolution Order，MRO）。类都有一个名为 __mro__ 的属性，它的值是一个元组，按照方法解析顺序列出各个超类。从当前类一直向上，直到 object 类。D 类的 __mro__ 属性如下：
End of explanation
def ping(self):
    A.ping(self) # 而不是 super().ping()
    print('post-ping', self)
Explanation: 若想把方法调用委托给超类，推荐的方法是使用内置的 super() 函数。在 Python 3 中，这种方式变得更容易了，如上面的 D 类中的 pingpong 方法所示。然而，有时可能幸亏绕过方法解析顺序，直接调用某个类的超方法 -- 这样有时更加方便。，例如，D.ping 方法可以这样写
End of explanation
d = D()
d.ping() # 输出了两行，第一行是 super() A 类输出，第二行是 D 类输出
Explanation: 注意，直接在类上调用实例方法时，必须显式传入 self 参数，因为这样访问的是未绑定方法（unbound method）
然而，使用 super() 最安全，也不易过时，调用框架或不受自己控制的类层次结构中的方法时，尤其适合用 super()。使用 super() 调用方法时，会遵循方法解析顺序，如下所示：
End of explanation
d.pingpong() #最后一个是直接找到 C 类实现 pong 方法，忽略 mro
Explanation: 下面看看 D 在实例上调用 pingpong 方法得到的结果，如下所示：
End of explanation
bool.__mro__
def print_mro(cls):
    print(', '.join(c.__name__ for c in cls.__mro__))
print_mro(bool)
import numbers
print_mro(numbers.Integral)
import io
print_mro(io.BytesIO)
print_mro(io.TextIOWrapper)
Explanation: 方法解析顺序不仅考虑继承图，还考虑子类声明中列出超类的顺序。也就是说，如果声明 D 类时把 D 声明为 class D（C, B)，那么 D 类的 __mro__ 就会不一样，先搜索 C 类，再 搜索 B 类
分析类时，我们需要经常查看 __mro__ 属性，下面是一些常用类的方法搜索顺序:
End of explanation
import tkinter
print_mro(tkinter.Text)
Explanation: 结束方法解析之前，我们再看看 Tkinter 复杂的多重继承：
End of explanation
<END_TASK>