python - 在dotdict

时间:2018-04-18 13:56:11

标签: python python-3.x copy deep-copy

我在应用程序周围的不同位置使用了dotdict来增强我的代码的可读性。我不知道这会导致很多问题。一个特别令人烦恼的情况是它似乎与副本库不兼容。

这就是我所说的dotdict

class DotDict(dict):
    """dot.notation access to dictionary attributes"""
    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

即。一种访问字典属性的方法:dictionary.attribute

当我尝试

nested_dico = DotDict({'example':{'nested':'dico'}})
copy.deepcopy(nested_dico)

我收到以下错误:

/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/copy.py in deepcopy(x, memo, _nil)
    167                     reductor = getattr(x, "__reduce_ex__", None)
    168                     if reductor:
--> 169                         rv = reductor(4)
    170                     else:
    171                         reductor = getattr(x, "__reduce__", None)

TypeError: 'NoneType' object is not callable

我认为这是因为它无法识别我的类DotDict,因此认为它是NoneType。

有没有人知道解决这个问题的方法?也许覆盖副本库的有效类型?

2 个答案:

答案 0 :(得分:4)

查看发生错误的位置,reductor不是None,但它是内置函数,这意味着错误发生在C代码的某处,我们无法看到回溯。但我的猜测是,它试图获得一个不确定存在的方法,如果没有就准备好捕获AttributeError。相反,这会调用.get而返回None而没有错误,因此它会尝试调用该方法。

这行为正确:

class DotDict(dict):
    """dot.notation access to dictionary attributes"""

    def __getattr__(self, item):
        try:
            return self[item]
        except KeyError as e:
            raise AttributeError from e

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

a = DotDict(x=1, b=2)

print(deepcopy(a))

我知道这与原始类的行为不同,并且您无法使用.作为可选键,但我认为有必要使用外部代码来避免类错误以可预测的方式行事。

编辑:这是一种实现深度复制并保留原始__getattr__

的方法
class DotDict(dict):
    """dot.notation access to dictionary attributes"""

    __getattr__ = dict.get
    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __deepcopy__(self, memo=None):
        return DotDict(deepcopy(dict(self), memo=memo))

试验:

dd = DotDict(x=1, b=2, nested=DotDict(y=3))

copied = deepcopy(dd)
print(copied)
assert copied == dd
assert copied.nested == dd.nested
assert copied.nested is not dd.nested
assert type(dd) is type(copied) is type(dd.nested) is type(copied.nested) is DotDict

答案 1 :(得分:0)

实现自定义数据结构的副本相当容易,如此

def my_dict_copy(d):
    return {k:v for k,v in d.items()}

d1 = { 'a' :1,'b':2}
d2 = my_dict_copy(d1)
d3 = d1
d1['a'] = 2

print(d1)
print(d2)
print(d3)

输出

{'a': 2, 'b': 2}
{'a': 1, 'b': 2}
{'a': 2, 'b': 2}

你没有提供你的dict实现,我认为它会响应像items()这样的类方法,如果没有,必须有一种方法可以迭代所有键和值

我还假设键和值是不可变对象而不是数据结构,否则你需要复制'k'和'v'(可能使用原点copy lib)