简体   繁体   English

python-在dotdict上使用copy.deepcopy

[英]python - using copy.deepcopy on dotdict

I have used dotdict in various locations around my app to enhance readability of my code. 我在应用程序周围的各个位置使用了dotdict来增强代码的可读性。 Little did I know that this would cause many problems down the road. 我几乎不知道这会在以后引起很多问题。 One particularly annoying case is the fact that it does not seem to be compatible with the copy library. 一个特别烦人的情况是它似乎与副本库不兼容。

This is what I mean by dotdict 这就是我的意思

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

ie a way of accessing dictionary attributes as such: dictionary.attribute 即一种访问字典属性的方法,例如: dictionary.attribute

When I try 当我尝试

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

I get the following error: 我收到以下错误:

/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

I assume this is because it does not recognise my class DotDict and thus considers it to be NoneType. 我认为这是因为它无法识别我的类DotDict,因此将其视为NoneType。

Does anyone know a way around this? 有谁知道解决这个问题的方法吗? Maybe override the copy library's valid types? 也许重写副本库的有效类型?

Looking into the spot where the error occurs, reductor is not None , but it's a built-in function, meaning the error occurs somewhere in C code where we can't see a traceback. 查看发生错误的地方, reductor不是None ,而是一个内置函数,这意味着错误发生在C代码中我们看不到追溯的地方。 But my guess is that it tries to get a method that it's not sure exists, ready to catch an AttributeError if not. 但是我的猜测是,它试图获取一个不确定的方法,如果不存在,则准备捕获AttributeError Instead this calls .get which returns None without an error, so it tries to call the method. 而是调用.get ,它返回None没有错误,因此它尝试调用该方法。

This behaves correctly: 这表现正确:

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))

I know that's not the same behaviour as your original class, and you won't be able to use . 我知道这与您的原始课堂不同,并且您将无法使用. for optional keys, but I think it's necessary to avoid errors like this with external code that expects your class to behave in a predictable way. 可选键,但我认为有必要避免这样的错误,因为外部代码期望您的类以可预测的方式运行。

EDIT: here is a way to implement deepcopy and preserve the original __getattr__ : 编辑:这是一种实现深度复制并保留原始__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))

Tests: 测试:

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

implementing a copy for custom data structure is fairly easy, smth like so 为自定义数据结构实现副本非常容易,就像这样

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)

output 输出

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

you didn't provided your implementation of dict, I assume it respond to class methods like items() if not, there must be a way you can iterate all the keys and values 您没有提供dict的实现,我假设它会响应诸如items()类的类方法,否则,必须有一种方法可以迭代所有键和值

also I assume the key and values are immutable objects and not data structure otherwise you need to make a copy of 'k' and 'v' also (maybe using the origin copy lib) 我也假设键和值是不可变的对象,而不是数据结构,否则您还需要制作“ k”和“ v”的copy (也许使用原始copy库)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM