python中一切都可以被继承,dict自然不例外,但是我们并不建议大家去继承Python中的内置的像list,dict等数据结构,特别是用C语言写的数据结构,因为有些操作不会生效。
1. 自定义一个字典时,不要直接继承自dict,因为有些操作会不生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyDict(dict):
def __setitem__(self, key, value):
# 重写父类的setitem方法,似乎并没有什么用
super(MyDict, self).__setitem__(key, value*5)

# 首先明确下面两种方式其本质是一致的操作,都会调用__setitem__这个魔法函数
mydict = MyDict(one=2)
print(mydict)
mydict['one'] =2
print(mydict)

# 运行结果:
{'one': 2}
{'one': 10}

也就是是说在某些情况下,用C语言写的Python内置的数据结构并不会去调用我们覆盖的方法,即方法重写失败,因此想要避免这个问题,可以继承collections模块的UserDict。

2、使用继承UserDict的方式来实现自定义的字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from collections import UserDict

class MyDict(UserDict):
def __setitem__(self, key, value):
super(MyDict, self).__setitem__(key, value*5)


mydict = MyDict(one=2)
print(mydict)

mydict['one'] =2
print(mydict)

# 运行结果:
# {'one': 10}
# {'one': 10}

我们可以对比一下,C语言写的dict中的__setitem__魔法函数,和用Python写的collections模块中UserDict的魔法函数__setitem__的区别:

C语言写的dict中的__setitem__魔法函数

1
2
3
def __setitem__(self, *args, **kwargs): # real signature unknown
""" Set self[key] to value. """
pass

Python写的collections模块中UserDict的__setitem__魔法函数

1
2
def __setitem__(self, key, item): 
self.data[key] = item

这两者是有区别的。
3、创建带有默认值的字典,使用collections模块中的defaultdict
这里主要介绍defaultdict的实现原理,字典之所以可以实现带有默认值,因为它内部实现了__missing__这个魔法方法,在UserDict类里面的__getitem__方法会调用__missing__方法。

我们查看一下UserDict的源码,发现先判断key是否存在data中,不存在则判断是否具有__missing__魔法函数,而defaultdict其实就是重写了__missing__魔法函数这个魔法函数:

1
2
3
4
5
6
def __getitem__(self, key):
if key in self.data:
return self.data[key]
if hasattr(self.__class__, "__missing__"):
return self.__class__.__missing__(self, key)
raise KeyError(key)

查看collections模块中的defaultdict的源码,发现它确实重写了__missing__这个魔法函数:

1
2
3
4
5
6
7
8
def __missing__(self, key): # real signature unknown; restored from __doc__
"""
__missing__(key) # Called by __getitem__ for missing key; pseudo-code:
if self.default_factory is None: raise KeyError((key,))
self[key] = value = self.default_factory()
return value
"""
pass

而且,若key不存在,则会调用这个default_factory()方法,查看一下它的源码:

1
2
default_factory = property(lambda self: object(), lambda self, v: None, lambda self: None)  # default
"""Factory for default value called by __missing__()."""

它会将这个值赋值给对象,property这个属性方法会在后面的元类编程中介绍。

1
2
3
4
5
6
7
8
9
10
11
from collections import defaultdict

my_dict = defaultdict(dict)
print(my_dict)

my_dict["name"] = "hello" # 这个defaultdict检测key不存在,就会调用__missing__魔法函数
print(my_dict)

# 运行结果:
# defaultdict(<class 'dict'>, {})
# defaultdict(<class 'dict'>, {'name': 'hello'})