【问题标题】:python - using copy.deepcopy on dotdictpython - 在dotdict上使用copy.deepcopy
【发布时间】:2018-04-18 13:56:10
【问题描述】:

我在我的应用程序周围的不同位置使用了 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。

有人知道解决这个问题的方法吗?也许覆盖复制库的有效类型?

【问题讨论】:

  • 什么是 dotdict,它从何而来?当__reduce__被访问时,你可以重写它来抛出一个AttributeError吗?
  • 请提供minimal reproducible example 并指向您正在使用的特定库。是github.com/vkuznet/DotDict吗?
  • 如果可能的话,看看导致错误的代码会很好吗?
  • 实现你自己的副本...遍历字典并返回一个新的
  • 问题已根据 cmets 更新。你能举个例子吗@shahaf

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


【解决方案1】:

查看错误发生的位置,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))

我知道这与您的原始课程的行为不同,您将无法将 . 用于可选键,但我认为有必要避免使用期望您的课程行为的外部代码出现此类错误以可预测的方式。

编辑:这是一种实现 deepcopy 并保留原始 __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

【讨论】:

    【解决方案2】:

    实现自定义数据结构的副本相当容易,就像这样

    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)

    【讨论】:

    • 我需要复制一个嵌套字典,其中包含许多不同的层,包含各种类型的数据类型(列表、数据框、字符串、字典...)
    • @Ludo 贴出所有需要复制的数据类型的代码,以便我们制定具体的解决方案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-12
    • 2010-11-27
    • 2012-05-03
    • 2019-09-29
    • 1970-01-01
    相关资源
    最近更新 更多