尝试创建一个自定义的不区分大小写的字典,我遇到了以下不方便和(从我的观点来看)意外行为。如果从dict
派生类,则在转换回__iter__
时会忽略重载的keys
,values
,dict
函数。我将它浓缩为以下测试用例:
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__
的值不一致,只是为了演示实际调用哪些方法。
documentation for dict.__init__
说:
如果给出了位置参数并且它是映射对象,则a 使用与映射相同的键值对创建字典 宾语。否则,位置参数必须是迭代器对象。
我想这与第一句话有关,也许对内置词典进行了优化。
为什么dict(d)
的调用不会使用keys
,__iter__
中的任何一个?
是否有可能以某种方式重载“映射”以强制dict
构造函数使用我的键值对的表示?
为什么我用这个?对于不区分大小写但保留字典,我想:
dict
以使用某些使用isinstance
支票的外部库代码如果您对应用案例感兴趣:here is corresponding branch
答案 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)
是否有可能以某种方式重载“映射”以强制dict构造函数使用我的键值对表示?
没有
作为一种固有的类型,重新定义dict的语义肯定会在其他地方造成彻底的破坏。
你有一个你不能覆盖dict
的行为的库,这很难,但重新定义语言原语并不是答案。你可能会觉得如果有人在背后加入整数加法的交换属性,那就太麻烦了;这就是为什么他们不能。
关于您的评论“UserDict
(正确)在False
支票中提供isinstance(d, dict)
”,当然这是因为它不是dict
和{ {1}}具有dict
无法保证的非常具体的不变量。