Python:是否可以进行“深度覆盖”?

时间:2016-02-22 17:11:53

标签: python python-3.x dictionary subclass

我想创建一个dict的子类,其中包含一个适用于所有嵌套dicts的自定义比较函数。此示例类忽略顶级关键字'j'的所有dict值,但在复制时不替换较低级别的dicts:

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替换每个dict,但是如果我必须遍历数据结构,我只是在递归中进行比较而不定义自定义类。

那么有没有办法定义自动替换基类的所有嵌入式实例的自定义子类?

2 个答案:

答案 0 :(得分:4)

您可以在您的网站上实施__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被用作深度复制一个自定义词典时被称为authomatically的deepcopy方法,但也可以用普通词典“bootstraped”,被称为独立的功能。

最后,与您需要的答案没有直接关系,在您的真实代码中,考虑继承自collections.UserDict而不是dict - 本机代码中有一些快捷方式可能会给您带来意外情况在你继承的类中。 (包括用于__eq__)的固有递归

答案 1 :(得分:0)

更简单的方法不需要复制数据,并且用子类替换所选dicts的递归是简短,明确且易于理解的。子类仅覆盖相等性测试,不需要__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 Schema的特定情况,以测试是否可以将多个属性合并到patternProperties中:

def replaceable(var):
    if 'type' in var and var['type'] == 'object' and \
        'properties' in var and isinstance(var['properties'],dict):
        return var['properties']
    return {}