为什么 foo['bar': 'baz'] 引发 TypeError 而不是 SyntaxError?

问题描述 投票:0回答:3

纯粹出于好奇而提出的问题。这显然是无效的语法:

foo = {}
foo['bar': 'baz']

很明显发生了什么,开发人员从字典定义中移出了一行,但没有将其从字面字典声明更改为赋值语法(因此已被适当地模拟)。

但我的问题是,为什么Python在这里提出

TypeError: unhashable type
而不是
SyntaxError
?它尝试散列什么类型?只是这样做:

'bar': 'baz'

是一个

SyntaxError
,就像这样:

['bar': 'baz']

所以我看不到正在创建的类型是不可散列的。

python dictionary typeerror slice
3个回答
65
投票

在索引操作中使用冒号生成一个

slice
对象,该对象不可散列。


22
投票

我只是想在Ignacio回答中添加一些细节(这很棒),这需要我一些时间来理解,对于像我这样没有得到它的人(我可能是唯一一个没有得到它的人)因为我没有看到有人问我不明白但怎么知道:) ) :

我第一次想知道什么片?字典索引不接受切片?

但这对我来说是一个愚蠢的问题,因为我忘记了python是动态的(我是多么愚蠢),所以当python第一次编译代码时,python不知道

foo
是字典还是列表,所以它只需将 foo['foo':'bar'] 这样的任何表达式作为切片读取,就知道您可以这样做:

def f():
    foo = {}
    foo['bar':'foo']

并且通过使用 dis 模块,您将看到表达式

'bar':'foo'
已自动转换为切片:

dis.dis(f)
  2           0 BUILD_MAP                0
              3 STORE_FAST               0 (foo)

  3           6 LOAD_FAST                0 (foo)
              9 LOAD_CONST               1 ('bar')
             12 LOAD_CONST               2 ('foo')
             15 SLICE+3             <<<<<<<<<<<<<<<<<<<<<< HERE!!!!!!            
             16 POP_TOP             
             17 LOAD_CONST               0 (None)
             20 RETURN_VALUE   

我第一次承认我没有考虑到这一点,我确实直接进入了python的源代码试图理解为什么,因为列表的

__getitems__
不像字典的
__getitem__
但现在我明白了为什么因为如果它是一个切片并且切片是不可散列的它应该提高
unhashable type
,所以这里是字典的代码
__getitem__

static PyObject *
dict_subscript(PyDictObject *mp, register PyObject *key)
{
    PyObject *v;
    long hash;
    PyDictEntry *ep;
    assert(mp->ma_table != NULL);   
    if (!PyString_CheckExact(key) ||                // if check it's not a string 
        (hash = ((PyStringObject *) key)->ob_shash) == -1) {
        hash = PyObject_Hash(key);    // check if key (sliceobject) is hashable which is false 
        if (hash == -1)
            return NULL;
    } 
    ....

希望这可以帮助像我这样的人理解伊格纳西奥的伟大回应,如果我只是重复伊格纳西奥的答案,抱歉:)


0
投票

表达式

foo[a:b]
foo[slice(a, b)]
的语法糖。也就是说,您使用
slice
对象对字典进行索引。无论
foo
是什么,这都是事实。

在 Python 3.11 及更低版本中,这会引发

TypeError
,因为切片对象不是 hashable。但是,从 Python 3.12(2023 年 10 月 2 日发布)开始,切片对象现在是可散列的1,使其用作字典键有效。

原始问题中的代码现在将引发

KeyError
而不是
TypeError

>>> foo = {}
>>> foo['bar': 'baz']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: slice('bar', 'baz', None)

现在也可以写了

foo = {}
foo["bar" : "baz"] = 1

生成字典

{slice('bar', 'baz', None): 1}


1 前提是

start
stop
step
都是可哈希的。

© www.soinside.com 2019 - 2024. All rights reserved.