背景介绍
在python中,我们经常使用字典类型实现映射表的功能,通过字典的主键遍历获取对应的值,从而实现从一个值映射到另一个值的功能
但是这种映射是十分硬性的,例如,假如我的映射表为{‘1’:one,‘2’:two},这是一个阿拉伯数字对应映射成英语的过程,在查找映射表的过程中,我的主键输入‘1’可以得到值one,但如果我输入的是1(即并不是加上引号的字符串类型而是数值类型),python的字典类型就会抛出主键错误的提示,因为在原字典中,‘1’是作为字符串类型的主键,1是作为整形类型的数据,两者在计算机中的储存格式是不相同的
如果我们希望得到一张映射表,既可以通过字符串类型遍历取值,也能通过对应的数值类型取值,那么应该如何实现呢?__missing__可以提供解决方案
__missing__方法
对于python中的dict类型,当我们需要通过主键获取对应的值时,k是主键,dict是字典类型,可以有dict[k]或者dict.get(k),都是调用字典类型中的__getitem__方法,遍历查找字典中对指定主键所对应的值,当没有找到主键时,则返回KeyError
另一种情况是,当__missing__方法存在时,就会调用__missing__方法中设定的操作,例如defaultdict类就是这种运行原理如何统计csv文件中对象的数量(基于pandas)-CSDN博客可以事先对不存在的字典主键赋予默认值,给在运行的过程带来方便
任务设定
为了让大家更好地理解__missing__方法,我们来简单设定一个任务,建立映射表{1:'one',2:'two',3:'three'},在建立映射的过程中,既可以使用1作为主键获取对应的值,也可以通过对应的字符串‘1’获取相应的值
代码实现
我们通过自定义类来实现,需要继承dict的类型
我们先来看看没有加__missing__方法的继承:
class UsualDict(dict):
def get(self,key,default=None):
try:
return self[key]
except KeyError:
return default
其中,get()函数就是调用__getitem__方法获取主键对应的值
dd=UsualDict({1:'one',2:'two',3:'three'})
print(dd[1])
输出>>one
print(dd['1'])
输出>>KeyError: '1'
可见没有实现任务的要求,我们再来使用__missing__方法:
class UsualDict(dict):
def __missing__(self,key):
return self[int(key)]
def get(self,key,default=None):
try:
return self[key]
except KeyError:
return default
print(dd['1'])
输出>>one
任务好像可以完成了
但是却有一个隐藏的漏洞
print(dd[5])
Traceback (most recent call last):
File "d:\py-code\fluent_pyhton\1_24.py", line 33, in <module>
print(dd[5])
File "d:\py-code\fluent_pyhton\1_24.py", line 25, in __missing__
return self[int(key)]
File "d:\py-code\fluent_pyhton\1_24.py", line 25, in __missing__
return self[int(key)]
File "d:\py-code\fluent_pyhton\1_24.py", line 25, in __missing__
return self[int(key)]
[Previous line repeated 496 more times]
RecursionError: maximum recursion depth exceeded
错误提示语说递归的层级已经超过了最大的允许范围,这是为什么呢?
我们回到代码,具体看这一句
def __missing__(self,key):
return self[int(key)]
只要__getitem__方法没有得到主键的内容我们就调用__missing__方法,调用__missing__方法后的self[int(key)]又会调用__getitem__方法,那么只要key的int类型或者str类型都不存在于字典的主键中,那么两个方法之间就会无限进行递归调用,那么这个bug需要如何解决呢?
可以有以下继承方法:
class UsualDict(dict):
def __missing__(self,key):
if isinstance(key,int):
raise KeyError(key)
return self[int(key)]
def get(self,key,default=None):
try:
return self[key]
except KeyError:
return default
通过isinstance方法判断key的类型是否属于int,即当__getitem__方法找不到主键调用__missing__方法时,首先判断key是否属于int,若属于int类型但又在字典主键中查找不到对应的值,则返回错误,否则,才进行key类型的转换
我们来看看是否还会出现递归的错误
dd=UsualDict({1:'one',2:'two',3:'three'})
print(dd[5])
系统进行了正确的错误输出
Traceback (most recent call last):
File "d:\py-code\fluent_pyhton\1_24.py", line 33, in <module>
print(dd[5])
File "d:\py-code\fluent_pyhton\1_24.py", line 24, in __missing__
raise KeyError(key)
KeyError: 5
欢迎大家讨论交流~