从dict派生时绕过重载的__iter__

时间:2013-08-19 15:47:23

标签: python dictionary overloading subclassing

尝试创建一个自定义的不区分大小写的字典,我遇到了以下不方便和(从我的观点来看)意外行为。如果从dict派生类,则在转换回__iter__时会忽略重载的keysvaluesdict函数。我将它浓缩为以下测试用例:

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}

keysvalues__iter____getitem__的值不一致,只是为了演示实际调用哪些方法。

documentation for dict.__init__说:

  

如果给出了位置参数并且它是映射对象,则a   使用与映射相同的键值对创建字典   宾语。否则,位置参数必须是迭代器对象。

我想这与第一句话有关,也许对内置词典进行了优化。

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

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

  • 在内部存储(小写=>(original_case,value)),同时显示为(any_case =>值)。
  • 派生自dict以使用某些使用isinstance支票的外部库代码
  • 不使用2个字典查找:lower_case => original_case,然后是original_case =>值(这是我现在正在做的解决方案)

如果您对应用案例感兴趣:here is corresponding branch

2 个答案:

答案 0 :(得分:2)

the file dictobject.c中,您会看到第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 ff。)区分了真正的dicts和其他对象。在真正的dicts的情况下,项目使用PyDict_GetItem()读出,dict是对象的“最内部接口”,并且不会使用任何用户定义的方法。

因此,您应该使用UserDict module

,而不是继承{{1}}

答案 1 :(得分:1)

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

没有

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

你有一个你不能覆盖dict的行为的库,这很难,但重新定义语言原语并不是答案。你可能会觉得如果有人在背后加入整数加法的交换属性,那就太麻烦了;这就是为什么他们不能。

关于您的评论“UserDict(正确)在False支票中提供isinstance(d, dict)”,当然这是因为它不是dict和{ {1}}具有dict无法保证的非常具体的不变量。