python - 为什么 UserDict 的 __copy__ 和 copy() 的行为不一致

标签 python python-3.7

UserDict Source Code

UserDict 有__copy__ 和.copy() 方法。前者由copy.copy(x) 触发,后者由x.copy() 触发。 在 copy() 中,它首先将 self.data 设置为 {},最后使用 c.update(self) 填写c.data。但是, update() 将触发 __setitem__ ,可能使 c.data 不同于 self.data

from collections import UserDict
class Foo(UserDict):
    def __setitem__(self, key, value):
        if isinstance(key, int):
            self.data[key] = value
a = Foo({1:2})
# we force it to set key of str type
a.data['3'] = 4
# two different ways to copy
from copy import copy
b = copy(a)
c  = a.copy()
# this copy(a) works fine, but a.copy() is not what we expected!!!
assert b == a
assert not c == a

为什么会存在这种不一致? 为什么不只是这个:

    def copy(self):
        if self.__class__ is UserDict:
            return UserDict(self.data.copy())
        import copy
        return copy.copy(self)

我不知道您可以在 Python3 中直接对 dict 进行子类化。 请参阅:How to "perfectly" override a dict? Advantages of UserDict class in Python . 无论如何,也许没有理由再为 UserDict 烦恼了。

请参阅提到的网站 Trey Hunner:https://treyhunner.com/2019/04/why-you-shouldnt-inherit-from-list-and-dict-in-python/

最佳答案

UserDict.__copy__UserDict.copy() 之间的预期区别由您链接的源代码中的注释解释。来自 UserDict.__copy__ :

# Create a copy and avoid triggering descriptors

该类的开发人员已确保 __copy__ 不会触发 descriptors .所以 UserDict.copy() 是触发描述符的补充方法。这是通过创建空字典的副本并将所有元素重新插入该副本来实现的。

请注意,UserDict 类很早就引入了,before Python 2.2 ,作为子类化字典的一种方式。当您实现一个旨在由技能水平不可预测的开发人员子类化的类时,您会采取预防措施让类实例以合理的方式运行,即使子类编写得不好。将副本创建为空字典并重新插入元素对我来说就像是一种安全编程。

另一种方法可能是按照您的建议从 .copy() 调用 .__copy__(),并写入子类应覆盖 的文档.copy() 触发描述符。但是,有多少子类开发人员会阅读文档的那部分内容、理解其含义、花时间重写该方法而不在其实现中引入错误?

关于python - 为什么 UserDict 的 __copy__ 和 copy() 的行为不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56621321/

相关文章:

python - 如何检测zlib是否可用以及ZIP_DEFLATED是否可用?

python - Anaconda Python 32 位正在尝试加载 Anaconda 64 位库

python - 在 python 3.7 中使用 paho mqtt 客户端的问题

python - BeautifulSoup:从 findAll ResultSet 中剥离 HTML 标签

python - 多次调用时如何停止长时间运行的函数?

python - 预期类型 'Iterable[SupportsLessThan | Any]' (匹配泛型类型 'Iterable[SupportsLessThanT]' ),却得到 'object'

python - pickle 转储的多处理队列问题

ubuntu - 如何在 Google Colab 上安装 gmpy2?

python - 推送被拒绝,无法编译 Python 应用程序。|我无法将 Django 应用程序部署到 Heroku

python - 使用模块 __getattr__ 时如何回退到默认值