Python 2.2之后内置类型开始可以子类化了
但是,CPython中的内置类型不会调用用户重写的类的特殊方法。
PyPy的文档中描述了这个问题。subclasses-of-built-in-types
正式情况下,CPython 并没有官方规定内置类型的子类中重写的方法是否会被隐式调用。基本上,这些用户重写的方法不会被同一对象的其他内置方法调用。例如,在dict的子类中重写的 `__getitem__()` 方法不会被内置的 `get()` 方法调用。以上情况在CPython和PyPy中都是一致的。在内置函数或方法是否会调用另一个对象的重写方法而不是self上,两者会出现差异。PyPy经常会在Cpython不调用的情况下调用。
两个例子:
ex1:
class D(dict):def __getitem__(self, key):return "%r from D" % (key,)class A(object):passa = A()
a.__dict__ = D()
a.foo = "a's own foo"
print a.foo
# CPython => a's own foo
# PyPy => 'foo' from D
ex2:
glob = D(foo="base item")
loc = {}
exec "print foo" in glob, loc
# CPython => base item
# PyPy => 'foo' from D
原生类型的这种行为违背了面向对象编程的一个基本原则:始终应该从实例(self)所属的类开始搜索方法,即使在超类实现的类中调用也是如此。(不过 __missing__
方法是个特例。)
在CPython的实现结构中,内建方法大部分会忽略用户重写的特殊方法,所以产生了UserDict,UserString,UserList来解决这个问题。
In : class NewDict(dict):
...: def __getitem__(self, key):
...: return 42
...:
In : d = NewDict(a=1)
In : d
Out: {'a': 42}
In : d2 = {}
In : d2.update(d) #CPython中的update会忽略NewDict中的__getitem__方法
In : d2
Out: {'a': 1}
因此用户如果自己定义的类应该继承collections模块中的类。而不是子类化内置类。
*《流畅的Python》 12.1