如果密钥不在some_dict中,为什么some_dict ['key'] = somevalue会工作?

时间:2017-08-18 11:44:53

标签: python dictionary operators

我知道我可以通过执行

在python dict中添加新的键/值
some_dict['absent_key'] = somevalue

但我真的不了解内部工作。

我曾经认为字典表现得像C ++地图。 []运算符将为给定键创建元素(如果它尚不存在),则返回对它的引用,以便可以在运算符=的同一行中为其赋值。 / p>

但是,C ++中的这种行为的结果是,如果我们从映射中查询不存在的键的值,则会为该键创建该元素,并且将返回值类型的默认值而不是一个错误。在python中,这会抛出KeyError

所以我不明白的是:如何,因为[]运算符必须在python中=之前进行评估(我认为?),它的行为会有所不同,具体取决于是否会读取结果或指定一个值(在表达式评估的那一点上它不应该知道)?

python评估表达式的顺序有区别吗?或者解释器只是更聪明,因为字典是一个硬编码类型,所以它更准确地知道它的行为,而std :: map是在'库'中,所以编译器可以假设更少?还是其他一些原因?

3 个答案:

答案 0 :(得分:5)

操作:

some_dict[key]

some_dict[key] = value

del some_dict[key]

使用对象的不同特殊方法:__getitem____setitem____delitem__。因此,不只是一个运算符([])实现了它们。

也许一个例子可以说明:

class Something(dict):  # subclassing dict
    def __getitem__(self, key):
        print('trying to get', key)
        return super().__getitem__(key)
    def __setitem__(self, key, value):
        print('trying to set', key, 'to', value)
        return super().__setitem__(key, value)
    def __delitem__(self, key):
        print('trying to delete', key)
        return super().__delitem__(key)

测试:

>>> s = Something({'a': 1, 'b': 2})
>>> s['a']
trying to get a
1

>>> s['c'] = 10
trying to set c to 10

>>> del s['b']
trying to delete b

所以这取决于它们的实施方式。在纯Python dict__getitem__只返回键的值,如果它不存在则抛出。

但是子类也可以实现__missing__方法 - 如果他们想要自定义行为,如果密钥没有出现在dict中(在查找期间)。

答案 1 :(得分:1)

幕后发生了什么?

在Python中,为键指定值时:

dictionary[key] = value

Python将上述语法糖翻译成:

dictionary.__setitem__(key, value)

如您所见,Python在后面调用__setitem__方法。 __setitem__方法直接对应于索引数据结构并为所述索引分配新值的操作。它可以重载以定制它的行为。

Python字典__setitem__的默认行为是更改密钥的值(如果存在),如果没有引发KeyError。为了证明这一点,您可以继承dict类并重载__setitem__以显示它的参数:

>>> class Dict(dict):
...     def __setitem__(self, key, value):
...         print('Putting "%s" in dict with value of "%s"' % (key, value))
...         super().__setitem__(key, value)
...
>>>
>>> d = Dict()
>>> d['name'] = 'Hammy'
Putting "name" in dict with value of "Hammy"
>>> d['age'] = 25
Putting "age" in dict with value of "25"
>>> d
{'name': 'Hammy', 'age': 25}

Python是否有std :: map等价物?

像@MSeifert所说,你可以通过重载__missing__方法来自定义当密钥不存在时会发生什么。

这就是collections.defaultdict类在标准库中的作用。它会重载__missing__以创建缺失的key,并将您选择的默认值映射到它。来自CPython源代码的Here's the relevant snippet

static PyObject *
defdict_missing(defdictobject *dd, PyObject *key)
{
    PyObject *factory = dd->default_factory;
    PyObject *value;
    /* ... */
    value = PyEval_CallObject(factory, NULL);
    if (value == NULL)
        return value;
    if (PyObject_SetItem((PyObject *)dd, key, value) < 0) {
        Py_DECREF(value);
        return NULL;
    }
    return value;
}

请注意,defaultdict在C中实现。以下是用法示例:

>>> from collections import defaultdict
>>> map = defaultdict(int)
>>> map['a'] = 1
>>> map['b'] = 2
>>> map['c'] # default factory function `int` called
0
>>> map
defaultdict(<class 'int'>, {'a': 1, 'b': 2, 'c': 0})

defaultdict几乎与std :: map :: operator []的行为相匹配。如果在使用std :: map :: operator []时没有键,则操作员调用&#34;工厂函数&#34;该值与键值的预期类型相匹配,并将其分配给缺失的键。

因此,如果您想要的行为类似于std :: map,请使用defaultdict。注意我说&#34;喜欢&#34;但是。这是因为C ++和Python 两种完全不同的语言。用一种语言说一个数据结构在另一种语言中有一个精确等价物很少是正确的。

答案 2 :(得分:0)

my_dict['key'] = 'value'表示法只是糖:

my_dict.__setitem__('key', 'value')

该功能完成存储数据的所有工作。但是,它可以实现。 python解释器和库使用的基本机制通常来自更快的编译语言,如C。

还有更多类似的功能,例如__len__()__getitem__(x)__delitem__(x),可以处理所有the other dict类似的操作。