为啥 Python 会引发 TypeError 而不是 SyntaxError?

Posted

技术标签:

【中文标题】为啥 Python 会引发 TypeError 而不是 SyntaxError?【英文标题】:Why does Python raise TypeError rather than SyntaxError?为什么 Python 会引发 TypeError 而不是 SyntaxError? 【发布时间】:2011-05-08 14:55:26 【问题描述】:

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

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

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

但我的问题是,为什么 Python 在这里提出 TypeError: unhashable type 而不是 SyntaxError?它试图散列什么类型?只是这样做:

'bar': 'baz'

是一个 SyntaxError,如下所示:

['bar': 'baz']

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

【问题讨论】:

【参考方案1】:

在索引操作generates a slice object 中使用冒号,这是不可散列的。

【讨论】:

我不得不承认,我没想到。 +1 Python 语法通常比我们想象的要灵活。【参考方案2】:

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

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

但这对我来说是一个愚蠢的问题,因为我忘记了 python 是动态的(我是多么愚蠢)所以当 python 第一次编译代码时 python 不知道 foo 是字典还是列表所以它只是读取任何像这样的表达式 foo['foo':'bar'] 作为 slice ,知道你可以这样做:

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

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

【讨论】:

我认为令人惊讶的是:我们立即看到'bar' 具有str 类型,因此这里不要考虑切片。但是 Python 在这一点上显然只看到它是一个字面量。如果它是 int 文字,则切片可能没问题——但此时显然尚未考虑文字的类型。 (通过 CPython;有人用 PyPy 尝试过吗?)

以上是关于为啥 Python 会引发 TypeError 而不是 SyntaxError?的主要内容,如果未能解决你的问题,请参考以下文章

为啥类创建会引发错误

为啥某些无效的 MIME 类型会触发“TypeError”,而其他无效的 MIME 类型会绕过错误并触发自动下载?

Django:“TypeError:[] 不是 JSON 可序列化的”为啥?

super() 为新型类引发“TypeError:必须是类型,而不是 classobj”

为啥 Python 中的“pip install”会引发 SyntaxError?

为啥返回 NotImplemented 而不是引发 NotImplementedError