为啥 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”