我想创建一个dict
子类,其中包括适用于所有嵌套词典的自定义比较函数。 此示例类忽略所有顶级键为"j"的字典值,但在创建副本时不会替换较低级别的字典:
import copy
p = {'a': 1, 'j': 2, 'c': [{'j':'cat','k':'dog'}]}
class udict(dict):
def __init__(self, x):
dict.__init__(self, copy.deepcopy(x))
def __eq__(self, other):
return all([self[k]==other[k] for k in set(self.keys())-set('j')])
a = udict(p)
b = udict(p)
a==b # True
b['j'] = 5
a==b # True - 'j' keys are imaginary and invisible
b['a'] = 5
a==b # False
b = udict(p)
b['c'][0]['j'] = 'bird'
a==b # False (should be True, but list contains dicts, not udicts)
我可以手动树遍历任意深度的数据结构,用 udict 替换每个字典,但如果无论如何我都必须遍历数据结构,我将只在递归中进行比较而不定义自定义类。
那么有没有办法定义一个自定义子类来自动替换基类的所有嵌入式实例呢?
__deepcopy__
您可以在您的自定义类:https://docs.python.org/2/library/copy.html -你将不得不"使用递归" - 但它仍然看到它会比你必须在那里做的任何其他事情都容易:
from copy import deepcopy
def custom_deepcopier(dct, memo=None):
result = MD()
for key, value in dct.items():
if isinstance(value, dict):
result[key] = MD(value)
else:
result[key] = deepcopy(value, memo)
return result
class MD(dict):
def __init__(self, x=None):
if x:
dict.__init__(self, custom_deepcopier(x))
def __eq__(self, other):
...
__deepcopy__ = custom_deepcopier
在以这种方式声明事物时,custom_deepcopier
既用作在深度复制自定义词典时自动调用的deepcopy
方法,也可以使用普通字典"引导",作为独立函数调用。
最后,与你需要的答案没有直接关系,在你的实际代码上,考虑从collections.UserDict
继承而不是dict - 字典的原生代码中有一些快捷方式,可能会给你继承的类带来不好的惊喜。(包括在用于__eq__
的固有递归中)
更简单的方法不需要复制数据,并且用子类替换所选字典的递归简短、明确且易于理解。 子类仅覆盖相等性测试,它不需要__init__
或__copy__
方法:
class MyDict(dict):
def __eq__(self, other):
return <custom equality test result>
def replaceable(var):
if <dict instance should be replaced by subclass instance>:
return <dict of instances to be replaced>
return {}
def replacedict(var)
if isinstance(var, list):
for i, v in enumerate(var):
var[i] = replacedict(v)
elif isinstance(var, dict):
for k, v in var.items():
var[k] = replacedict(v)
rep = replaceable(var)
for k, v in rep.items():
rep[k] = MyDict(v)
return(var)
对于测试 JSON 架构以测试是否可以将多个属性合并到模式属性的特定情况:
def replaceable(var):
if 'type' in var and var['type'] == 'object' and
'properties' in var and isinstance(var['properties'],dict):
return var['properties']
return {}