当从dict导出时,重载__iter__被绕过 [英] overloaded __iter__ is bypassed when deriving from dict

查看:212
本文介绍了当从dict导出时,重载__iter__被绕过的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

尝试创建自定义不区分大小写的字典,我遇到了以下不方便(从我的观点)意外的行为。如果从 dict 导出类,则重载的 __ iter __ keys 函数在转换回 dict 时将被忽略。我已将其精简到以下测试用例:

Trying to create a custom case-insensitive dictionary, I came the following inconvenient and (from my point-of-view) unexpected behaviour. If deriving a class from dict, the overloaded __iter__, keys, values functions are ignored when converting back to dict. I have condensed it to the following test case:

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 __ 只是为了演示哪些方法不一致实际上被调用。

The values for keys,values and __iter__,__getitem__ are inconsistent only for demonstration which methods are actually called.

的文档dict .__ init __ 说:


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

If a positional argument is given and it is a mapping object, a dictionary is created with the same key-value pairs as the mapping object. Otherwise, the positional argument must be an iterator object.

我想这与第一个句子有关,也可能是优化内置字典。

I guess it has something to do with the first sentence and maybe with optimizations for builtin dictionaries.

为什么调用 dict(d)不使用任何 key __ iter __
可以通过某种方式重载映射来强制 dict 构造函数来使用我的键值对的演示?

Why exactly does the call to dict(d) not use any of keys, __iter__? Is it possible to overload the 'mapping' somehow to force the dict constructor to use my presentation of key-value pairs?

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

Why did I use this? For a case-insensitive but -preserving dictionary, I wanted to:


  • store(lowercase =>(original_case,value)显示为(any_case =>值)。

  • 派生自 dict ,以便使用使用 isinstance 检查

  • 不使用2个字典查找:lower_case => original_case,其次是original_case => value(这是我现在正在做的解决方案)

  • store (lowercase => (original_case, value)) internally, while appearing as (any_case => value).
  • derive from dict in order to work with some external library code that uses isinstance checks
  • not use 2 dictionary lookups: lower_case=>original_case, followed by original_case=>value (this is the solution which I am doing now instead)

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

If you are interested in the application case: here is corresponding branch

推荐答案

文件 dictobject.c ,你看到的是1795ff。相关代码:

In the file dictobject.c, you see in line 1795ff. the relevant code:

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;
}

这告诉我们,如果对象有一个属性,被调用的代码只是一个合并。在那里调用的代码(l。1915 ff。)区分了真实的dict和其他对象。在实际的情况下,用 PyDict_GetItem()读取这些项目,这是该对象的最内在的接口,并且不会使用任何用户 -

This tells us that if the object has an attribute keys, the code which is called is a mere merge. The code called there (l. 1915 ff.) makes a distinction between real dicts and other objects. In the case of real dicts, the items are read out with PyDict_GetItem(), which is the "most inner interface" to the object and doesn't bother using any user-defined methods.

所以不要继承 dict ,你应该使用 UserDict 模块

So instead of inheriting from dict, you should use the UserDict module.

这篇关于当从dict导出时,重载__iter__被绕过的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆