Python 如果某个键不在某个dict中,为什么某个dict[';key';]=somevalue会起作用?

Python 如果某个键不在某个dict中,为什么某个dict[';key';]=somevalue会起作用?,python,dictionary,operators,Python,Dictionary,Operators,我知道我可以通过执行以下操作在python dict中添加一个新的键/值 some_dict['absent_key'] = somevalue 但我并不真正理解内部工作 我过去认为字典的行为就像C++的地图。如果给定键不存在,[]运算符将为其创建元素,然后返回对该元素的引用,以便可以在与运算符=相同的行中为其分配值 但是C++中的行为有这样的结果,如果我们从一个map中查询一个不存在的关键字的值,那么该元素就被创建用于那个键,而返回值类型的默认值而不是一个错误。在python中,这会抛出一个

我知道我可以通过执行以下操作在python dict中添加一个新的键/值

some_dict['absent_key'] = somevalue
但我并不真正理解内部工作

我过去认为字典的行为就像C++的地图。如果给定键不存在,
[]
运算符将为其创建元素,然后返回对该元素的引用,以便可以在与运算符
=
相同的行中为其分配值

<>但是C++中的行为有这样的结果,如果我们从一个map中查询一个不存在的关键字的值,那么该元素就被创建用于那个键,而返回值类型的默认值而不是一个错误。在python中,这会抛出一个
keyrerror

所以我不明白的是:既然python中的
[]
操作符也必须在
=
之前进行求值(我想是吧?),那么它的行为是否会有所不同,这取决于结果是被读取还是被赋值(在表达式求值时它不应该知道)

python计算表达式的顺序是否有差异?或者解释器更聪明,因为字典都是硬编码类型,所以它更准确地知道它的行为,而std::map在“库”中,所以编译器可以承担更少的任务?还是其他原因?

操作:

some_dict[key]

使用对象的不同特殊方法:、和。所以实现它们的不仅仅是一个操作符(
[]

也许一个例子可以说明:

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)
测试:

因此,这取决于它们是如何实现的。在普通Python中,dicts
\uuu getitem\uuu
只返回键的值,如果不存在,则抛出

但是子类也可以实现这个方法,以防在dict中没有键(在查找过程中)的情况下,它们想要自定义行为。

幕后发生了什么? 在Python中,将值分配给键时:

dictionary[key] = value
Python将上述语法转换为:

dictionary.__setitem__(key, value)
如您所见,Python在幕后调用
\uuuuu setitem\uuu
方法。
\uuuu setitem\uuuu
方法直接对应于为数据结构编制索引并为所述索引分配新值的操作。它可以重载以自定义其行为

Python字典的
\uuuuu setitem\uuuu
的默认行为是更改键的值(如果存在),如果不存在,则引发
键错误
。为了证明这一点,您可以对
dict
类进行子类化,并重载
\uuuuu setitem\uuuuuu
以显示其参数:

>>> 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所说的,您可以通过重载
\uuu missing\uu
方法来定制当密钥不存在时发生的情况

这就是类在标准库中所做的。它重载
\uuuu missing\uuuu
以创建缺少的
,并将您选择的默认值映射到该键。从CPython来源:

静态PyObject*
defdict_缺失(defdictobject*dd,PyObject*键)
{
PyObject*factory=dd->默认工厂;
PyObject*值;
/* ... */
value=PyEval\u CallObject(工厂,空);
如果(值==NULL)
返回值;
if(PyObject_SetItem((PyObject*)dd,key,value)<0){
Py_DECREF(值);
返回NULL;
}
返回值;
}
请注意,
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
>>>map=defaultdict(int)
>>>映射['a']=1
>>>映射['b']=2
>>>map['c']#调用了默认工厂函数'int'
0
>>>地图
defaultdict(,{'a':1,'b':2,'c':0})
defaultdict
与std::map::operator[]的行为非常匹配。如果在使用std::map::operator[]时键不存在,则该运算符调用与键的值的预期类型匹配的“工厂函数”,并将其分配给缺少的键


因此,如果您想要类似std::map的行为,请使用
defaultdict
。注意,我说的是“喜欢”。这是因为C++和Python是<强>两个完全不同的语言< /强>。说一种语言的数据结构与另一种语言的数据结构完全相同是非常不正确的。
my_dict['key']='value'
表示法仅用于:

my_dict.__setitem__('key', 'value')
该函数完成存储数据的所有工作。但是,您可以根据需要实现它。python解释器和库使用的底层机制通常来自更快的编译语言,如C


还有更多类似这样的函数,
\uuuu len\uuuuuuuuuuuuuuo()
\uuuuu getitem\uuuuuuuuuuux
,它们处理所有类似的操作。

在lhs上,
[]
被映射到
\uu setitem\uuuuuuuux(key,rhs)
在rhs
[/code>上映射到
\uu getitem(key)
,它们实现了不同的逻辑。在C++中,代码> []/COD>是它自己的操作符,它返回一个可以更新的引用。python中的
=
是一个语句,
lhs
不是像
C++
中那样的表达式。旁注:访问操作符
[]
不是必需的:您可以执行
dictionary.get(key,如果找不到key,则某些可选的默认值)
。酷,这也意味着我可以对dict进行子类化,并重载getitem()要让它像std::map一样运行,如果我想:)@bartoli Yes。但我认为您不需要为此创建自己的子类:Pythons标准库中也提供了。不确定它是否与std::map相同,但它“更接近”。)@MSeifert我认为这种行为非常接近;-)请看这里:。@ChristianDean在比较不同语言的数据结构时,我总是很小心。如果你使用绝对值(a语言相当于b语言相当于a语言),有人会戳它,并提出100个(一些有效的,一些…不)不相等的理由。只是我的前男友
>>> 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}
>>> 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})
my_dict.__setitem__('key', 'value')