Python 当从dict导出时,重载的iter被绕过

Python 当从dict导出时,重载的iter被绕过,python,dictionary,overloading,subclassing,Python,Dictionary,Overloading,Subclassing,在尝试创建一个自定义的不区分大小写的字典时,我遇到了以下不方便且(在我看来)意外的行为。如果从dict派生类,则在转换回dict时,将忽略重载的\uu iter\uuu、键、值函数。我已将其压缩为以下测试用例: import collections class Dict(dict): def __init__(self): super(Dict, self).__init__(x = 1) def __getitem__(self, key):

在尝试创建一个自定义的不区分大小写的字典时,我遇到了以下不方便且(在我看来)意外的行为。如果从
dict
派生类,则在转换回
dict
时,将忽略重载的
\uu iter\uuu
函数。我已将其压缩为以下测试用例:

import collections

class Dict(dict):
    def __init__(self):
        super(Dict, self).__init__(x = 1)

    def __getitem__(self, key):
        return 2

    def values(self):
        return 3

    def __iter__(self):
        yield 'y'

    def keys(self):
        return 'z'

    if hasattr(collections.MutableMapping, 'items'):
        items = collections.MutableMapping.items
    if hasattr(collections.MutableMapping, 'iteritems'):
        iteritems = collections.MutableMapping.iteritems

d = Dict()
print(dict(d))              # {'x': 1}
print(dict(d.items()))      # {'y': 2}
iter
getitem
的值仅在演示实际调用的方法时不一致

报告说:

如果给定了位置参数且该参数是映射对象,则 使用与映射相同的键值对创建字典 对象否则,位置参数必须是迭代器对象

我想这与第一句话有关,也可能与内置词典的优化有关

为什么对
dict(d)
的调用没有使用任何
? 是否可能以某种方式重载“映射”以强制
dict
构造函数使用我的键值对表示法?

我为什么用这个?对于不区分大小写但保留大小写的词典,我想:

  • 内部存储(小写=>(原始大小写,值)),同时显示为(任意大小写=>值)
  • dict
    派生,以便使用使用
    isinstance
    检查的某些外部库代码
  • 不使用2个字典查找:小写字母=>original字母,后跟original字母=>value(这是我现在正在做的解决方案)

如果您对应用程序案例感兴趣:

是否可能以某种方式重载“映射”以强制dict构造函数使用我的键值对表示法?

没有


作为一种固有类型,重新定义dict的语义肯定会在其他地方造成彻底的破坏

你已经有了一个库,你不能覆盖中的
dict
行为,这很难,但重新定义语言原语并不是答案。如果有人在你背后搞砸了整数加法的交换性质,你可能会觉得很讨厌;这就是为什么他们不能

至于你的评论“
UserDict
(正确)在
isinstance(d,dict)
检查中给出了
False
”,当然是这样,因为它不是
dict
dict
有非常具体的不变量,而
UserDict
无法保证。

在第1795ff行中,你可以看到。有关守则:

static int
dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, char *methname)
{
    PyObject *arg = NULL;
    int result = 0;

    if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg))
        result = -1;

    else if (arg != NULL) {
        _Py_IDENTIFIER(keys);
        if (_PyObject_HasAttrId(arg, &PyId_keys))
            result = PyDict_Merge(self, arg, 1);
        else
            result = PyDict_MergeFromSeq2(self, arg, 1);
    }
    if (result == 0 && kwds != NULL) {
        if (PyArg_ValidateKeywordArguments(kwds))
            result = PyDict_Merge(self, kwds, 1);
        else
            result = -1;
    }
    return result;
}
这告诉我们,如果对象有一个属性
keys
,则调用的代码只是一个合并。在那里调用的代码(l.1915及其后)区分了真实的dict和其他对象。对于真正的dict,使用
PyDict_GetItem()
读取项目,这是对象的“最内部接口”,不需要使用任何用户定义的方法


因此,您应该使用。

当下次将继承的字典转换回简单的dict对象时,为什么要创建继承的字典,而不是从
dict
继承?我的意思是,一旦你使用
dict(d)
它就会转换回普通的字典。你应该使用:
d.items()
作为一种固有类型,重新定义
dict
的语义肯定会在其他地方引起一些惊讶(如果不是彻底破坏的话)。我不太理解为什么这个问题被标记为主要基于观点。第二个答案和第一个评论是(无论如何,这甚至得到了投票)。然而,公认的答案陈述了“事实”,即由于实施细节的原因,这是不可能的。此外,这个问题的答案远非显而易见,因为在面向对象语言中,这是意外的行为,不可能重写字典
\uu iter\uu
。我明白了,问题在于
Pydict\u Check()
。我觉得实现不仅应该检查继承,还应该检查重写的方法,以便产生更一致的行为。继承自
UserDict
(正确)在
isinstance(d,dict)
检查中给出False,这就是我想从
dict
派生的原因。奇怪,我在您的答案中看到的使用链接的源代码与您显示的不完全相同,行号也不同……在这种情况下,似乎可以通过使用返回正确值的
\uuuuuu getitem\uuuuuo()
方法创建一个派生类来使其工作。@martineau确实很奇怪;在我写这篇文章的时候,我似乎对这个文件有了一个不同的版本。我只是将链接更改为一个concreate版本,所以我们不应该再得到任何Dispeparance了。但是由于调用链是
dict\u init()->dict\u update\u common()->PyDict\u Merge()->PyDict\u Merge()
,重写
\uu getitem\uuuuuuuuuuuu()
也不会有帮助。这就是dict使用的
类型(d)。要准确获得
dict
的保证。对于子类(
isinstance
),您希望它们可以覆盖功能。这不正是多形性的全部意义吗?如果你想争论语言应该是另一种语言,我建议你提交一份报告。你问了一个关于语言的问题,我回答了;很抱歉,你不喜欢这个答案,但这并不影响它的有效性。