2

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

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}

和的值不一致keys,仅用于演示实际调用了哪些方法。values__iter____getitem__

文档dict.__init__说:

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

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

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

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

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

如果您对应用案例感兴趣:这里是对应的分支

4

2 回答 2

2

文件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 的情况下,项目是用 读出的PyDict_GetItem(),这是对象的“最内部接口”,不需要使用任何用户定义的方法。

dict因此,您应该使用UserDict模块而不是继承自。

于 2013-08-19T16:11:36.187 回答
1

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

不。

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

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

关于您的评论“ UserDict(正确)False进行isinstance(d, dict)检查”,当然可以,因为它不是一个dict并且dict具有UserDict无法保证的非常具体的不变量。

于 2013-08-19T16:01:12.897 回答