Python:可以做一个"deep class override"吗?

Python: Is it possible to do a "deep class override"?

我想创建 dict 的子class,其中包含适用于所有嵌套字典的自定义比较函数。此示例 class 忽略所有在顶层具有键 '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 替换每个字典,但是如果我无论如何都必须遍历数据结构,我将只在递归中进行比较而不定义自定义 class.

那么有没有一种方法可以定义自定义子class,自动替换基础class的所有嵌入实例?

您可以在您的设备上实施 __deepcopy__ 方法 自定义 class: https://docs.python.org/2/library/copy.html - 你将不得不 "use recursion" - 但它仍然认为它比你在那里必须做的任何其他事情都容易:

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 方法,也可以 "bootstraped" 与普通字典,作为独立函数调用。

最后,与您需要的答案没有直接关系,在您的真实代码中,考虑从 collections.UserDict 继承而不是 dict - dict 的本机代码中有一些快捷方式可能会带来糟糕的惊喜在您继承的 classes 中为您服务。 (包括用于 __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 {}