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
),您希望它们可以覆盖功能。这不正是多形性的全部意义吗?如果你想争论语言应该是另一种语言,我建议你提交一份报告。你问了一个关于语言的问题,我回答了;很抱歉,你不喜欢这个答案,但这并不影响它的有效性。