37 votes

Pourquoi Python lève-t-il TypeError plutôt que SyntaxError ?

Une question par pure curiosité. Il s'agit manifestement d'une syntaxe invalide :

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

Ce qui s'est passé est évident : le développeur a déplacé une ligne de la définition du dictionnaire, mais n'a pas modifié la déclaration littérale du dictionnaire pour passer à la syntaxe d'affectation (ce qui lui a valu les moqueries appropriées).

Mais ma question est de savoir pourquoi Python soulève TypeError: unhashable type ici plutôt que SyntaxError ? Quel type tente-t-il de hacher ? Je fais juste ça :

'bar': 'baz'

est une SyntaxError, tout comme ceci :

['bar': 'baz']

donc je ne peux pas voir quel type est créé qui n'est pas hachable.

62voto

Ignacio Vazquez-Abrams Points 312628

Utilisation des deux points dans une opération d'indexation génère un slice objet qui n'est pas hachable.

21voto

mouad Points 21520

Je veux juste ajouter quelques détails à Réponse d'Ignacio (ce qui est génial) et cela m'a pris du temps à comprendre et pour les gens comme moi qui n'ont pas compris (je suis peut-être le seul à ne pas avoir compris parce que je n'ai vu personne me demander si je n'avais pas compris, mais qui sait ?) ) :

la première fois, je me demande ce qu'est une tranche ? l'indexation du dictionnaire n'accepte pas les tranches ?

mais c'est une question stupide de ma part parce que j'ai oublié que python est dynamique (comme je suis stupide) donc quand python compile le code pour la première fois, il ne sait pas si le code est dynamique ou non. foo est un dictionnaire ou une liste, il peut donc lire n'importe quelle expression comme foo['foo':'bar'] comme une tranche, pour savoir que vous pouvez simplement faire :

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

et en utilisant dis vous verrez que l'expression 'bar':'foo' a été automatiquement converti en tranche :

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   

la première fois, j'admets que je n'ai pas pensé à cela et que je suis allé directement au code source de python pour essayer de comprendre pourquoi, parce que l'option __getitems__ de la liste n'est pas comme __getitem__ d'un dictionnaire mais maintenant je comprends pourquoi parce que si c'est une tranche et que la tranche n'est pas hachable, il devrait soulever unhashable type Voici donc le code du dictionnaire __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;
    } 
    ....

J'espère que cela pourra aider certaines personnes comme moi à comprendre la réponse d'Ignacio, et je suis désolé si je ne fais que répéter la réponse d'Ignacio :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X