使用ChainMap
最近看到组内有小伙伴在处理在多个dict内顺序查找元素的时候,用了ChainMap,有意思。场景: 当用户输入一个产品id的时候,你希望能正在出售的产品,或在已过期的产品中查找。
你可以实现:
g_expire_products = {
1: {'name': 'nike shoes', 'price': 30},
2: {'name': 'box', 'price': 2},
}
g_on_sale_products = {
3: {'name': 'adidas shoes', 'price': 25},
4: {'name': 'tee', 'price': 2},
}
def get_product(id):
return g_on_sale_products.get(id, g_expire_products.get(id))
如果是不止已过期产品,希望在其他库房的产品也能被查找,你可能还要在一个新的dict里面查找。
这不难实现,就是代码看起来有点难看。
为何不试下ChainMap,用ChainMap可以这样写:
g_products = ChainMap(g_on_sale_products, g_expire_products)
def get_product(id):
return g_products.get(id)
ChainMap可以添加多个dict,相当于把你的if-else判断做了抽象。
ChainMap源码
ChainMap源码在这儿
可以看下__init__
def __init__(self, *maps):
'''Initialize a ChainMap by setting *maps* to the given mappings.
If no mappings are provided, a single empty dictionary is used.
'''
self.maps = list(maps) or [{}] # always at least one map
这里传入多个dict后,多个dict放在maps里,这里maps传的是多个dict的引用,也就是它没有拷贝内存。
那么它是如何查找key的,我们来看下:
def __missing__(self, key):
raise KeyError(key)
def __getitem__(self, key):
for mapping in self.maps:
try:
return mapping[key] # can't use 'key in mapping' with defaultdict
except KeyError:
pass
return self.__missing__(key) # support subclasses that define __missing__
def get(self, key, default=None):
return self[key] if key in self else default
def __contains__(self, key):
return any(key in m for m in self.maps)
可以看到,当通过dict[key]的时候,它是从self.maps里的存的dict,从前往后找,初始化的时候,第一个dict先被查找,key不在第一个dict的时候,就会找下一个dict里查找。
不过它在修改的时候,是这样做的
def __setitem__(self, key, value):
self.maps[0][key] = value
def __delitem__(self, key):
try:
del self.maps[0][key]
except KeyError:
raise KeyError('Key not found in the first mapping: {!r}'.format(key))
也就是你修改其中一个key的时候,它会在第一个dict里面修改,删除一个key时候也是。
所以一般使用ChainMap的时候,我觉得最好不要做修改,因为修改是基于你初始化的时候,传入的dict的顺序,每次如果初始化的时候,如果dict的顺序不确定,你每次就不能确定会修改哪一个dict。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。