Journal le défi du challenge : qu'affiche ce code

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes :
13
1
déc.
2024

Demat'iNal

c'est l'époque des sondages on dirait… alors, d'après toi, qu'affiche l'exécution de ce code Python :

e = 1
try:
    raise NotImplementedError
except NotImplementedError as e:
    pass
print(type(e))
  1. <class 'int'>
  2. SyntaxError
  3. <class 'NotImplementedError'>
  4. NameError
  • # Troy

    Posté par  . Évalué à 0 (+0/-1).


    < Troy >


         \   ^__^ 
          \  (oo)\_______
             (__)\       )\/\\
                 ||----w |
                 ||     ||
    
  • # Je joue le jeu

    Posté par  . Évalué à 2 (+0/-0).

    Mon raisonnement complet est un peu fouilli. Mais de base, e devrait être 1 en sortie du bloc try-except, parce que les variables locales ne devraient pas déborder. Après, peut-être que les exceptions sont une… exception ?

    Mais un truc me turlupine, c'est le raise NotImplementedError sans parenthèse. Je ne suis pas sûr que ça marche, j'ai l'impression qu'il faut instancier l'erreur, ce n'est pas fait par le raise.

    Je vote 2, puis 1.

    Je vais exécuter ce code, et bien sûr, je ne vais rien dire de plus ici :)

    • [^] # Re: Je joue le jeu

      Posté par  . Évalué à 1 (+0/-0).

      Je ne pense pas qu'un bloc try/execpt crée un nouveau contexte (nouvelle portée de variable).

    • [^] # Re: Je joue le jeu

      Posté par  (site web personnel) . Évalué à 4 (+2/-0).

      Moi je vote 3 mais je vais tester pour être sûr.

      Python recèle bien des surprises quand on le chatouille un peu.

      Je me souviens d'un PyCon où qq'un proposait ce genre de challenge:

      Quelle est la valeur retournée :

      try:
          return 1
      finally:
          return 2

      Variante :

      try:
          raise ValueError
          return 1
      except ValueError:
          return 2
      finally:
          return 3

      D'ailleurs, je ne me souviens plus de la réponse et je suis bien en peine de la trouver de nouveau sans un test…

      • [^] # Re: Je joue le jeu

        Posté par  . Évalué à 3 (+4/-3).

        Dans les trucs totalement illogiques de python, et plus à ras de terre, j'ai toujours admiré celui-ci :

        a = b = c = 10
        c **= 2
        a += c
        b += c
        print(a is b)  # True
        c **= 2
        a += c
        b += c
        print(a is b)  # False

        D'accord, c'est documenté, et ce test a is b est un peu un cas d'école sans application pratique ici, du coup on lui préfèrera a == b quand on a affaire à des entiers, mais bon, ça en dit long sur la rigueur qui a présidé à la création de ce langage.

        • [^] # Re: Je joue le jeu

          Posté par  . Évalué à 7 (+4/-0).

          t'as la même en java

          var a = new Integer(126);
          var b = new Integer(126);
          System.out.println(a==b);
          a++;
          b++;
          System.out.println(a==b);
          a++;
          b++;
          System.out.println(a==b);

          qui donne un résultat complétement absurde si tu connais pas la mécanique interne. (et oui je sais qu'il faut utiliser a.equals(b) et non == sur des Integer)

          Il ne faut pas décorner les boeufs avant d'avoir semé le vent

        • [^] # Re: Je joue le jeu

          Posté par  (site web personnel) . Évalué à 3 (+2/-0).

          Ce comportement est plus dû à une optimisation de l'interpréteur cpython sur les petits nombres qu'à un manque de rigueur du langage. Il est basé sur ce comportement:

          a = 1
          b = 1
          print(a is b)  # True
          c = 2000
          d = 2000
          print(c is d)  # False
          • [^] # Re: Je joue le jeu

            Posté par  (site web personnel) . Évalué à 5 (+2/-0).

            Pour a et b entre -5 et 256 ça vaut True, et False sinon (dis comme ça ça fait très nombres magiques sortis du chapeau).

          • [^] # Re: Je joue le jeu

            Posté par  . Évalué à 4 (+2/-0).

            Oui, je sais bien, c'est pour ça que j'ai dit que c'est documenté, et Benoît en a donné les limites exactes. Mais présenté sous la forme où je l'ai mise, ça montre vraiment l'absurdité de ce comportement, et le peu de fiabilité que donne l'opérateur "is" avec les entiers. Parce qu'on ne connaît pas dans le déroulement d'une routine la valeur qu'aura "c", dans cet exemple.

            Tu es sûr que ça vient de cpython ? Je crois plutôt que ça vient de la conception de python, qui dit que chaque entier est une classe, donc a=1 et b=1 fait qu'ils pointent vers la même classe, donc a is b est vrai. Mais passé une certaine limite, ça n'est plus vrai, ça ne sont plus des classes, je ne me rappelle plus pour quelle raison, mais ça a probablement à voir avec le fait qu'on ne peut avoir une infinité de classes pré-définies.

            Il suffisait de ne pas définir les entiers comme classes, après tout les floats n'en sont pas, et a = 1.0 ; b = 1.0 ; a is b renvoie False.

            • [^] # Re: Je joue le jeu

              Posté par  (site web personnel) . Évalué à 2 (+0/-0).

              C'est juste qu'on teste l'identité dans des cas où ça a un sens, pas n'importe quand.

              Chaque entier est une instance d'une classe, pas une classe (comme… les flottants). Y'a des choses à revoir.

              Votez les 30 juin et 7 juillet, en connaissance de cause. http://www.pointal.net/VotesDeputesRN

            • [^] # Re: Je joue le jeu

              Posté par  . Évalué à 2 (+0/-0).

              Je crois plutôt que ça vient de la conception de python, qui dit que chaque entier est une classe, donc a=1 et b=1 fait qu'ils pointent vers la même classe, donc a is b est vrai. Mais passé une certaine limite, ça n'est plus vrai, ça ne sont plus des classes, je ne me rappelle plus pour quelle raison, mais ça a probablement à voir avec le fait qu'on ne peut avoir une infinité de classes pré-définies.

              Ce sont des objets et c'est parce que a=1 b=1 c'est le même objet parce qu'il y a un cache d'objet. Est-ce que ce cache est une spécificité du runtime ou c'est décrit dans la définition du langage (et pour ce dernier cas est-ce que les bornes sont elles aussi définie) ça je ne sais pas. Quoi qu'il en soit le fait que le is fonctionne sur de petits entiers n'est pas un comportement garanti (exemple https://ideone.com/C4huhz).

              https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll

    • [^] # Re: Je joue le jeu

      Posté par  (site web personnel, Mastodon) . Évalué à 3 (+2/-0).

      La variable ayant été instantiée avant le bloc, elle est globale. Pour moi c'est 3. Je ne vois pas d'erreur flagrante. C'est relativement "simple"… sauf que peut-être que je me trompe.

      Sous licence Creative common. Lisez, copiez, modifiez faites en ce que vous voulez.

  • # Ma réponse va vous surprendre...

    Posté par  . Évalué à 2 (+0/-0).

    (avec l'encodage qui qui va bien pour éviter de spoiler… mes excuses aux rotteurs natifs)

    Gh crhk rger cyhf cerpvf qr pr dhr gh ragraqf cne "rkrphgvba" ?
    Wbyv oht qnaf yn ercy ra gbhg pnf :-)

  • # réponse 4

    Posté par  (site web personnel) . Évalué à 10 (+9/-1).

    d'après la doc, 8.4.1 except clause :

    When an exception has been assigned using as target, it is cleared at the end of the except clause. This is as if

    except E as N:
       foo

    was translated to

    except E as N:
        try:
            foo
        finally:
            del N

    This means the exception must be assigned to a different name to be able to refer to it after the except clause. Exceptions are cleared because with the traceback attached to them, they form a reference cycle with the stack frame, keeping all locals in that frame alive until the next garbage collection occurs.

    • [^] # Re: réponse 4

      Posté par  (site web personnel) . Évalué à 2 (+0/-0).

      Ah, je savais pas qu'on pouvait faire del $VARIABLE ; je pensais que c'était juste une syntaxe pour les dictionnaires… (del hache[qui])

      • [^] # Re: réponse 4

        Posté par  (site web personnel) . Évalué à 3 (+1/-0).

        d'après mon interprétation de la doc pour del ("Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a global statement in the same code block."), je pense qu'on peux voir del bla comme un sucre syntaxique pour del locals()["bla"]

        E.g.:

        >>> a = 1
        >>> a
        1
        >>> locals()
        {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'a': 1}
        >>> del locals()["a"]
        >>> a
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        NameError: name 'a' is not defined
  • # Ça dépend

    Posté par  (site web personnel) . Évalué à 6 (+3/-0).

    Mettons que le code en question soit dans un fichier coin. Alors choisir python3 coin ou python3 -i < coin permet de changer sa réponse.

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.