Python 3.8 : opérateur d’assignation, REPL async, Pickle v5 et plus

Posté par  . Édité par Ysabeau 🧶 🧦, palm123, Davy Defaud, Benoît Sibaud, patrick_g et Pierre Jarillon. Modéré par Ysabeau 🧶 🧦. Licence CC By‑SA.
Étiquettes :
80
15
oct.
2019
Python

Łukasz Langa vient d’annoncer au nom de la PSF la disponibilité de CPython 3.8, l’implémentation de référence. Cette version est particulière puisqu’elle introduit une syntaxe controversée : l’assignation avec l’opérateur := qui permet d’assigner dans une expression et plus seulement dans un statement.

Le nombre de changements est assez impressionnant. Malgré la maturité de Python et sa popularité, il y a encore à faire !

Pour les systèmes stables, la version de référence reste la 3.6. Pour les autres, profitez de pyenv ou Docker pour utiliser les nouveautés de ce bon cru ! Découvrez‐les dans la suite de cette dépêche.

Sommaire

Opérateur d’assignation

Jusqu’à présent, Python refusait tout simplement d’assigner une variable dans un if ou équivalent. Concrètement, if var = True: lève une erreur de syntaxe. Cela évite l’erreur classique d’assigner une valeur en oubliant le deuxième égal de l’opérateur de comparaison ==. Dans les langages autorisant l’assignation dans les expressions, l’utilisation de condition Yoda aidait à se défendre d’une erreur humaine. Dans Python, c’est, de toute façon, impossible.

Mais voilà, il y a certains cas où l’assignation et le test sont très liés. Et l’opérateur d’assignation est très pratique. Le cas le plus courant est l’exécution d’expression rationnelle :

m = re.match('motif', 'valeur')
if m is not None:
    ...

À partir de Python 3.8, un opérateur particulier permet d’assigner et de vérifier la valeur : l’opérateur :=. Les Anglo‑Saxons l’appellent walrus, c’est‐à‐dire morse (comme l’animal), car il ressemble à un smiley représentant les yeux et les défenses du morse. Désormais, on peut écrire le code précédant comme suit :

if (m := re.match('motif', 'valeur')) is not None:
    ...

La concision de cet opérateur est appréciable. Et il est difficile de le confondre avec l’opérateur de comparaison.

Le grand débat

Cet opérateur a été l’occasion d’un débat passionné. Est‐ce que cet opérateur est une variation de == ou bien faut‐il réutiliser le mot‑clef as comme dans except Exception as e ?

Guido van Rossum, créateur du langage, a utilisé son super‐pouvoir pour imposer le petit morse. Mais cela lui a coûté et il a démissionné de son poste de BDFL.

Arguments exclusivement positionnels

Python permet déjà de déclarer des arguments de fonction exclusivement nommés. Python 3.8 ajoute la possibilité de déclarer des arguments exclusivement positionnels. Voici un exemple :

def f(a, /, b):
    print(a, b)

f(0, 1)  # valide
f(a=0, b=1)  # invalide car a est nommé
f(0, b=1)  # valide

Certaines API en tireront parti pour simplifier la validation des arguments. À noter, le nom d’un argument positionnel peut être réutilisé en argument nommé. Par exemple :

def f(a, /, **kw):
  print(a, kw)

f(0, a=1)  # valide ! Affiche: '0 {"a": 1}'

Interpréteur async

Cette nouveauté n’est pas mise en avant, pourtant elle est bien pratique. En exécutant le module asyncio, CPython propose désormais un interpréteur Python acceptant le mot clef await pour exécuter une co‑routine :

Avant :

$ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> await asyncio.sleep(1)
  File "<stdin>", line 1
SyntaxError: 'await' outside function
>>>

Désormais :

$ python -m asyncio
asyncio REPL 3.8.0
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> await asyncio.sleep(1, result='hello')
hello

On pouvait avoir à peu près lʼéquivalent en lançant une boucle par co‑routine :

$ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> asyncio.run(asyncio.sleep(1, result='hello'))
'hello'
>>>

À suivre !

Pickle v5

Pickle est un protocole de transmission de données dans un format interne à Python. Le Français Antoine Pitrou a proposé une version 5 du protocole permettant de référencer des données transmises hors bande. Cela permet d’utiliser un canal de communication plus performant pour les gros volumes de données qu’on veut transmettre à Python.

Petit rappel, Pickle n’est pas un format pour communication publique. Il est très facile d’injecter du code dans votre programme via ce protocole. Utilisez‐le uniquement pour les communications internes à vos projets.

Et plus

Le mini‐langage de formatage de chaîne accepte un nouveau spécificateur f-string : {var=} qui affiche le nom de la variable et sa valeur. Cela économise une duplication lorsqu’on écrit des messages de débogage.

CPython peut maintenant stocker les fichiers bytecode dans un dossier de cache spécifique, ailleurs que dans des dossiers __pycache__ un peu partout. Il faut pour cela définir le paramètre PYTHONPYCACHEPREFIX via différentes méthodes. Cela permet, par exemple, de livrer du code source en lecture seule, sans précompiler, tout en laissant la possibilité d’avoir un cache de bytecode parallèle.

Le travail du Français Victor Stinner sur l’optimisation de l’appel de fonction a été amélioré avec un nouveau protocole d’exécution nommé vectorcall. Pour le moment, c’est de la cuisine interne. Python 3.9 devrait exposer davantage d’optimisation possible dans le code Python.

On peut désormais spécifier un caractère Unicode par son nom dans les expressions rationnelles avec \N{NOM UNICODE} plutôt que par son code ou son encodage UTF-8. Ainsi, r'\N{EM DASH}' équivaut à '—' ou '\u2014'. La notation \N permet d’utiliser le mode brut r'' incompatible avec \uXXXX.

python -m json.tool accepte désormais un objet JSON par ligne avec l’option --json-line. Ce cas est courant avec les journaux au format JSON.

Il reste moult nouveautés et changements à découvrir. N’hésitez pas à consulter la longue page des nouveautés de cette version 3.8, notamment la section sur les API obsolètes retirées et les conseils de migration. Bonne découverte !

Aller plus loin

  • # Merci

    Posté par  . Évalué à 10.

    J'ai trouvé le résumé vraiment bien fait avec un bon équilibre ni trop synthétique ni trop bavard.

    • [^] # Commentaire supprimé

      Posté par  . Évalué à 10.

      Ce commentaire a été supprimé par l’équipe de modération.

  • # Version de référence

    Posté par  . Évalué à 2.

    Pour les systèmes stables, la version de référence reste 3.6.

    Pourquoi pas 3.7 ? Python ne fait pas de différence entre les versions paires et impaires.

    • [^] # Commentaire supprimé

      Posté par  . Évalué à 10.

      Ce commentaire a été supprimé par l’équipe de modération.

      • [^] # Re: Version de référence

        Posté par  . Évalué à 5.

        Pour donner un cas concret, Django 3 aura le support des version supérieur à 3.6.

      • [^] # Re: Version de référence

        Posté par  . Évalué à -1. Dernière modification le 17 octobre 2019 à 17:36.

        Je ne sais pas s'il a compris, mais moi non.

        edit: finalement je crois qu'en fait si.

  • # Coquille

    Posté par  . Évalué à 0. Dernière modification le 16 octobre 2019 à 07:59.

    Les Anglo-saxons l’appellent walrus, c’est-à-dire morse (comme l’animal), car il ressemble à un smiley représentant les yeux est les défenses du morse.

    Le est devrait être et.

    Très bonne dépêche sinon, merci !

    • [^] # Re: Coquille

      Posté par  (site web personnel) . Évalué à 2.

      Corrigé, merci.

    • [^] # Re: Coquille

      Posté par  (site web personnel, Mastodon) . Évalué à 3.

      Il y a une petite coquille dans le code aussi, le / manque dans le deuxième exemple de « Arguments exclusivement positionnels »:

      def f(a, /, **kw):
        print(a, kw)
      
      f(0, a=1)  # valide ! Affiche: '0 {"a": 1}'

      Merci pour la dépêche en tout cas, elle résume bien sans aller dans tous les détails comme l'annonce officielle.

      • [^] # Re: Coquille

        Posté par  (site web personnel, Mastodon) . Évalué à 3.

        Corrigé merci.

        Oui c'est une dépêche agréable à lire (et à modérer).

        « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

    • [^] # Re: Coquille

      Posté par  . Évalué à 1.

      Autre petite coquille dans la section "Et plus" :

      Cela perme,t par exemple,

      Cela permet, par exemple.

  • # Yoda ?

    Posté par  (site web personnel) . Évalué à 6.

    Jusqu’à présent, Python refusait tout simplement d’assigner une variable dans un if ou équivalent. Concrètement if var = True: lève une erreur de syntaxe. Cela évite l’erreur classique d’assigner une valeur en oubliant le deuxième égal de l’opérateur de comparaison ==. Dans les langages autorisant l’assignation dans les expressions, l’utilisation de condition Yoda aidait à se défendre d’une erreur humaine.

    Qu'est-ce que c'est que cette condition Yoda ? Je comprends la référence, ça doit évoquer une expression formée de termes qui dans un ordre inhabituel sont, mais concrètement, ce que ça signifie je ne comprends pas. Un exemple ?

    • [^] # Commentaire supprimé

      Posté par  . Évalué à 10.

      Ce commentaire a été supprimé par l’équipe de modération.

      • [^] # Re: Yoda ?

        Posté par  . Évalué à 8.

        J'avais bien compris ça, mais c'est étrange comme nom quand on y pense. Yoda dirait plutôt "valeur" mavar == ("À valeur ma variable est égale"), non ?

        • [^] # Re: Yoda ?

          Posté par  . Évalué à 10.

          C'est une erreur de script de Star Wars. C'est compliqué à corriger upstream, même dans une version majeure, tant ça aurait de conséquences question rétro-compatibilité.

        • [^] # Commentaire supprimé

          Posté par  . Évalué à 5.

          Ce commentaire a été supprimé par l’équipe de modération.

      • [^] # Re: Yoda ?

        Posté par  (site web personnel) . Évalué à 3.

        Est-ce que ça réponds à ta question ?

        Oui, merci.

    • [^] # Re: Yoda ?

      Posté par  . Évalué à 6.

      Tu met la constante à gauche pour empêcher tout risque d'affectation. C'est le fait d'écrire :

      if (12 == foo) {
      }

      au lieu de :

      if (foo == 12) {
      }

      Tout ça pour éviter un malencontreux :

      if (foo = 12) {
      }

      Qui est toujours vrai. Il y a une page wikipedia là dessus.

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

      • [^] # Re: Yoda ?

        Posté par  . Évalué à 2.

        En même temps, c'est plus un problème sur un langage interprété.

        Ça fait des lustres que les compilateurs lèvent un warning sur ce type d'erreurs sans aller vers la nécessité d'un "décodeur maître Yoda" visuel!

        A ce sujet, ayant passé l'essentiel de ma vie à faire du C et ne faisant du python qu'occasionnellement (outils/test au taf ou domotique à la maison), je ne crois pas avoir vu abordé dans les dépêches les outils d'analyse syntaxiques simples et efficaces, s'il y en a, qui permettent d'éviter de ne voir les erreurs à l’exécution (et encore, si on fait en sorte de passer dans la bonne branche de code)?

        Bon, ça ramènera 30 ans en arrière quand passer un coup de lint pouvait faire gagner beaucoup de temps vu les temps de compilation de l'époque… Mais si j'en avais testé quelques uns pour Python, adopté aucuns: Trop de faux positifs, configuration pénible (visant de gros projets).

        • [^] # Re: Yoda ?

          Posté par  (site web personnel) . Évalué à 7.

          Ça fait des lustres que les compilateurs lèvent un warning sur ce type d'erreurs sans aller vers la nécessité d'un "décodeur maître Yoda" visuel!

          L'équivalent en Python sont les "linters" comme pylint, pyflakes, pycodestyle, etc. Il existe aussi des analyseurs de code plus orientés sécurité comme bandit, PyT, etc.

          Python se met aussi à lever des warnings à la compilation (et pas juste à l'exécution) :

          >>> def func(arg):
          ...  return arg is "a"
          ... 
          <stdin>:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
          
          >>> def func():
          ...  return "%s %s" ("hello", "world")
          ... 
          <stdin>:2: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?
          
          • [^] # Re: Yoda ?

            Posté par  . Évalué à 1. Dernière modification le 18 octobre 2019 à 14:12.

            Je me rappelle en avoir essayé quelques uns (je parlais d'ailleurs d'outils lint pour le C, désormais tombés en désuétude, j'avais naturellement cherché un équivalent), mais la richesse syntaxique de Python ne me semblait pas être ici un atout: Bien trop de faux positifs à mon goût.

            Pourtant, mon usage de Python étant limité, je ne crois pas faire des trucs tordus.. Peut-être pas assez "pythonic" en fait.

            • [^] # Re: Yoda ?

              Posté par  (site web personnel) . Évalué à 2.

              Peut-être que ton besoin serait plutôt autour de l'annotation de type. Jette un coup d'oeil à mypy et aux tutoriaux sur l'annotation de type (et à ma modeste conférence sur le sujet) pour voir si ça te parle plus.

        • [^] # Re: Yoda ?

          Posté par  . Évalué à 0.

          J'ai pas tout compris, mais, si tu pense que le C est immunisé à ce genre de choses, il faudra en parler aux développeur du noyau linux : https://freedom-to-tinker.com/2013/10/09/the-linux-backdoor-attempt-of-2003/

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

          • [^] # Re: Yoda ?

            Posté par  . Évalué à 4. Dernière modification le 18 octobre 2019 à 14:00.

            En même temps, depuis 2003, les chaînes de compil ont un peu évolué niveau verbosité de warning/erreur et particulièrement pour GCC, intelligibilité.

            Le C laisse beaucoup de liberté et c'est très bien ainsi car là ou il est le plus utilisé c'est soit inévitable soit s'en passer a un rapport bénéfice/risque difficilement justifiable.

            Alors même si tous les 5 ans on nous annonce son successeur en mieux (dont souvent des langages objet, pourtant au final toujours exclus depuis qu'ils existent car impossible d'y gérer finement l'utilisation mémoire), je pense que c'est bien le seul langage enseigné dont on peut assurer qu'il sera encore là quand ceux qui sortent d'école d'ingé actuellement prendront leur retraite!

      • [^] # Re: Yoda ?

        Posté par  (site web personnel) . Évalué à 2.

        Je n'aime pas du tout cette manière d'écrire, c'est répandu, mais illisible. Tu te dis "est-ce que cette variable vaut 12", et tu dois écrire "est-ce que 12 vaut cette variable" ? On doit écrire pour la lisibilité. La manière d'éviter cela c'est d'activer les warnings du compilateur.

        • [^] # Re: Yoda ?

          Posté par  (site web personnel) . Évalué à 1.

          Tourne la phrase autrement : "Est-ce que 12 est le résultat de…" :-)

        • [^] # Re: Yoda ?

          Posté par  . Évalué à 1.

          Je ne comprends pas. L'égalité marche toujours dans les deux sens. En maths, tu as plus de mal avec "12 = 2*5+2" qu'avec "2*5+2 = 12" ? Quand tu apprend à résoudre des équations tu apprend à manipuler les 2 côtés de l'égalité, je vois pas comment inverser les opérandes te troubles ?

          Si c'est qu'une histoire de vocalisation, tu dois bien pouvoir te trouver une phrase qui te plaira.

          Dans beaucoup de cas les assertions utilisent cet ordre.

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

          • [^] # Re: Yoda ?

            Posté par  (site web personnel) . Évalué à 4.

            L'égalité marche toujours dans les deux sens.

            Je me doute. Mais quand tu manipules une variable, c'est elle qui est l'élément essentiel.

            En maths, tu as plus de mal avec "12 = 2*5+2" qu'avec "2*5+2 = 12" ?

            Justement, en math, combien de fois tu as écris sur ta copie 0 = x plutôt que x = 0 ? Si tu écris x = 0 (avec x dans le terme de gauche, donc), c'est parce que ce qui t'intéresse, c'est la variable. C'est ce que tu mentionnes en premier. Si je te donne une variable, tu peux avoir son contenu, ici 0. Mais si je te donne un 0, il y a une infinité de variables qui pourraient avoir cette valeur. Ce n'est pas bijectif. Là on est dans le cas de l'assignation et pas dans le test, mais c'est tellement similaire que même si l'opérateur de comparaison est commutatif, dans les faits, la partie qui m'intéresse je veux l'écrire en premier, donc à gauche de l'opérateur.

            Si c'est qu'une histoire de vocalisation, tu dois bien pouvoir te trouver une phrase qui te plaira.

            Bin non, je ne veux pas. C'est changer le mode de pensée du développeur pour contourner une difficulté du langage. C'est à la machine de s'adapter à l'humain, pas l'inverse. Le code machine, c'est pour l'ordinateur, moi je suis un humain et je veux du code lisible, sans avoir à m'adapter à une méthode de contournement bidon.

            Le code est avec des noms d'instructions en anglais, pour être quasiment lu comme de l'anglais,

            Dans beaucoup de cas les assertions utilisent cet ordre.

            Pour la même raison foireuse. C'est aux compilateurs ou analyseurs statiques de t'avertir des tournures dangereuses.

            • [^] # Re: Yoda ?

              Posté par  . Évalué à 2.

              Justement, en math, combien de fois tu as écris sur ta copie 0 = x plutôt que x = 0 ? Si tu écris x = 0 (avec x dans le terme de gauche, donc), c'est parce que ce qui t'intéresse, c'est la variable. C'est ce que tu mentionnes en premier. Si je te donne une variable, tu peux avoir son contenu, ici 0. Mais si je te donne un 0, il y a une infinité de variables qui pourraient avoir cette valeur. Ce n'est pas bijectif. Là on est dans le cas de l'assignation et pas dans le test, mais c'est tellement similaire que même si l'opérateur de comparaison est commutatif, dans les faits, la partie qui m'intéresse je veux l'écrire en premier, donc à gauche de l'opérateur.

              C'est un super mauvais exemple : tu as des langages qui inversent l'affectation. Le premier qui me vient en tête c'est TI-Basic (https://fr.wikipedia.org/wiki/TI-Basic).

              Bin non, je ne veux pas. C'est changer le mode de pensée du développeur pour contourner une difficulté du langage. C'est à la machine de s'adapter à l'humain, pas l'inverse. Le code machine, c'est pour l'ordinateur, moi je suis un humain et je veux du code lisible, sans avoir à m'adapter à une méthode de contournement bidon.

              Il y a un moment où il faut travailler la plasticité de son cerveau aussi. Se créer des biais et passer temps à les maintenir ça n'a rien de sain.

              Si ta comparaison est faite via une fonction du type a.equals(b) ou equals(a, b), tu te sent perdu ?

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

              • [^] # Re: Yoda ?

                Posté par  (site web personnel) . Évalué à 2. Dernière modification le 12 novembre 2019 à 19:01.

                C'est un super mauvais exemple : tu as des langages qui inversent l'affectation.

                Et ? Le fait qu'un langage implémente cela dans sa syntaxe validerait cette approche. Dans ce cas les créateurs du brainfuck ont validé plein de choses. Ensuite il y a une différence entre la syntaxe d'un langage (obligatoire) et une convention qu'on est libre d'adopter ou pas.

                Il y a un moment où il faut travailler la plasticité de son cerveau aussi. Se créer des biais et passer temps à les maintenir ça n'a rien de sain.

                C'est cette inversion d'opérandes qui est un bias pour compenser la mauvaise utilisation du langage…

                Toi qui prenais l'exemple des Texas Instrument, j'ai utilisé une HP48 et utilisé en notation polonaise inversé (RPN). C'est une approche avec laquelle j'ai complètement adhéré (et à laquelle j'adhère toujours), et là pour le coup c'est vraiment une feature qui mérite le changement de mode de raisonnement.

                Mais là on parle d'une convention comme méthode de contournement pour un problème pour lequel le compilo remonte déjà un warning, et un -Werror te remontera donc obligatoirement tous les cas problématiques:

                    ../main.c:11:9: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
                         if (i = 0)
                             ^
                

                Le compilo te suggère de doubler les parenthèses pour expliciter le fait que oui, tu souhaites utiliser le résultat de cette affectation comme valeur pour ton test. Pas besoin de plus.

  • # Commentaire supprimé

    Posté par  . Évalué à 10.

    Ce commentaire a été supprimé par l’équipe de modération.

  • # Python se rapproche du Perl ?

    Posté par  (site web personnel) . Évalué à 10.

    Je suis un grand fan de Python mais je suis légèrement réservé sur les évolutions du langage ces dernières années.

    Quand j'ai commencé le Python, on était en 1.5.2 , et n'importe quel programmeur C, C++ ou Java pouvait lire du code Python sans jamais en avoir vu avant. Ces dernières années, on a fait évoluer la syntaxe de Python pour obtenir des possibilités très intéressantes, mais la syntaxe devient de moins en moins lisible.

    Allez, je me lance pour un concentré de truc imbitable:

    def f(a, /, d, *, e):
        l: Dict[int,int] = { 5:9, **{ v: v**2 for v in d if (t:=v%2) } }
        return l
    

    Est-ce que quelqu'un qui n'a jamais fait de Python peut me dire à quoi ça correspond ? A mon avis non. Même moi, j'ai du mal quand je vois ça. Pourtant, c'est tout à fait légitime en Python 3.8 . Et si on enlève le t:= à la fin et le / dans l'appel de fonction, ça marche même en Python 3.7 .

    Python à ses débuts avait notamment pris soin d'éviter les opérateur trop cryptiques pour les remplacer par des mot-clés (typiquement "and" au lieu de "&&", …). C'était une caractéristiques distinctives de Perl à l'époque qui raffolait des opérateurs imbitables et illisibles à base de caractères imprononçables. Ça soulageait aussi ma vision astigmate.

    Je regrette que le "power Python" ressemble de plus en plus à du Perl.

    Si j'écris du Python qui doit être relu par mes collègues, je suis attentif à éviter toutes les syntaxes puissantes car j'ai besoin qu'ils comprennent et soient en mesure de modifier mon code.

    On est en train de prendre la route du C++, ou deux personnes peuvent toutes les deux prétendre savoir coder en C++ et n'avoir que 10% de savoir en commun !

    • [^] # Commentaire supprimé

      Posté par  . Évalué à 10.

      Ce commentaire a été supprimé par l’équipe de modération.

      • [^] # Re: Python se rapproche du Perl ?

        Posté par  . Évalué à 8.

        Je trouve aussi.

        J'ai pas le temps, mais ça donnerait envie de relire les échanges pour comprendre pour pas as.

        Peut-être que Devuan pourrait distribuer un fork de Python qui utilise as.

        • [^] # Commentaire supprimé

          Posté par  . Évalué à 3. Dernière modification le 16 octobre 2019 à 14:51.

          Ce commentaire a été supprimé par l’équipe de modération.

          • [^] # Re: Python se rapproche du Perl ?

            Posté par  (site web personnel) . Évalué à 9.

            La PEP 572 liste les différentes alternatives proposées et donc rejetées.
            https://www.python.org/dev/peps/pep-0572/#alternative-spellings

            • [^] # Re: Python se rapproche du Perl ?

              Posté par  . Évalué à 5.

              J'ai beau lire, je trouve ça triste. Compliquer un langage simple, pour fluidifier à la marge 2 pauvres cases d'usage, qui avec deux lignes feront toujours l'affaire et seront plus flexible à travailler et à comprendre.

              • [^] # Re: Python se rapproche du Perl ?

                Posté par  (site web personnel) . Évalué à 4. Dernière modification le 06 novembre 2019 à 17:32.

                C'est surtout que l'exemple initial est discutable:
                https://www.python.org/dev/peps/pep-0572/#the-importance-of-real-code

                Another example illustrates that programmers sometimes do more work to save an extra level of indentation:

                    match1 = pattern1.match(data)
                    match2 = pattern2.match(data)
                    if match1:
                        result = match1.group(1)
                    elif match2:
                       result = match2.group(2)
                    else:
                        result = None

                This code tries to match pattern2 even if pattern1 has a match (in which case the match on pattern2 is never used). The more efficient rewrite would have been:

                    match1 = pattern1.match(data)
                    if match1:
                        result = match1.group(1)
                    else:
                        match2 = pattern2.match(data)
                        if match2:
                            result = match2.group(2)
                        else:
                            result = None

                Avec ce code "amélioré", on se retrouve avec autant de if/else imbriqués et donc de niveaux d'indentation qu'on a de motifs…

                Pour éviter l'indentation, j'aurais fait une sous-fonction et fait du early return:

                    match1 = pattern1.match(data)
                    if match1:
                        return match1.group(1)
                
                    match2 = pattern2.match(data)
                    if match2:
                       return match2.group(2)
                
                    return None

                Après j'ai pas de soucis avec la nouvelle syntaxe que je découvre aujourd'hui, mais il y a peut être des bonnes pratiques ou avertissement issus d'analyseurs statiques de code qui auraient pu aider à corriger ce problème plutôt que le langage lui même.

                • [^] # Re: Python se rapproche du Perl ?

                  Posté par  . Évalué à 3.

                  Oui effectivement,

                  Le seul cas pratique, j'avoue malgré moi même, c'est pour les boucles

                      with fp = open('python.exe', 'rb'):
                          while data := fp.read(1024):
                              ...
                  

                  En lieu et place de ce que j'ai tendance à écrire :

                      with fp = open('python.exe', 'rb'):
                          while True:
                              data := fp.read(1024):
                              if len(data) == 0:
                                  break
                              ...
                  

                  Pas tant pour le nombre de ligne, mais plutôt pour la sémantiquement. Ça me pique toujours un peu d'écrire une boucle "infinie". Je pouvais vivre avec :-)

      • [^] # Re: Python se rapproche du Perl ?

        Posté par  . Évalué à 2.

        Mouais… Cela doit dépendre d’où l'on vient car le := me semble pour ma part plus clair.
        Visiblement, les discutions ont été agitées au point de tuer le père! En arriver là, vu de dehors, ramène un peu à Sigmund.

    • [^] # Re: Python se rapproche du Perl ?

      Posté par  (site web personnel, Mastodon) . Évalué à 7.

      Le / dans les arguments est une possibilité du langage qui peut être pratique mais qui n'a pas forcément (et ne devrait pas à mon avis) à être utilisé souvent.

      Je vois 2 cas principaux où c'est utile et ça peut empêcher des problèmes:

      • imiter une API C, je pense que c'est le cas d'utilisation principal
      • pouvoir réutiliser des noms d'arguments dans kwargs. C'est un cas de niche, mais ça m'est déjà arrivé d'être embêté de ne pas pouvoir utiliser un nom d'une variable positionnelle dans des **kwargs, et le jour où ça arrive, on est bien content d'avoir cette syntaxe.

      De toute façon, comme le rappelle le zen de Python (import this), « Explicit is better than implicit. »: ces syntaxes sont à utiliser quand elle sont vraiment nécessaires, et dans les autres cas il vaut mieux quelques lignes de plus pour rendre le code plus lisible.

      Quelqu'un qui veut rendre un code illisible y arrivera toujours, Python ou pas.

    • [^] # Re: Python se rapproche du Perl ?

      Posté par  (site web personnel) . Évalué à 5.

      J'ai la même réaction. J'imagine qu'on ne gagne pas des masses sur les performances, et on perd en lisibilité ce qu'on gagne en écriture (alors qu'on écrit une seule fois mais on lit plusieurs fois : c'est plutôt le second qu'il faut optimiser).

    • [^] # Re: Python se rapproche du Perl ?

      Posté par  . Évalué à 3.

      Perl n'a pas d'arguments nommés, tous ces arguments sont positionnels et finissent dans @_.
      Pour contourner le problème on peux passer un hash en référence.

      Bon en fait je trouve :

          def f(a, /, b):
              print(a, b)
      

      Très moche (pour du Python) … tout comme les opérateurs *args et **kwargs, mais vu qu'il n'y a pas de préfixe de variable en python pour les typer ad-hoc, ils ont choisi de flaguer les arguments et en plus de rajouter un séparateur '/' arbitraire.

      Dommage.

    • [^] # Re: Python se rapproche du Perl ?

      Posté par  . Évalué à 3.

      Intrigué par le concentré de truc imbitable et supposant que c'est valide, peut-on avoir une petite explication ?

      • [^] # Re: Python se rapproche du Perl ?

        Posté par  (site web personnel) . Évalué à 10.

        Avec plaisir. Alors, l'explication de texte:

        def f(a, /, d, *, e):
            l: Dict[int,int] = { 5:9, **{ v: v**2 for v in d if (t:=v%2) } }
            return l
        

        Pour ce qui des arguments de la fonction f: / comme epxliqué dans la dépêche implique que a peut être passé uniquement en paramètre positionnel, d peut être passé un paramètre positionnel ou par mot-clé et e ne peut peut être passé que par mot-clé.

        Concrètement:

        >>> f({},{},{})
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: f() takes 2 positional arguments but 3 were given
        >>> f({},{},e={})   # ca marche
        {5: 9}
        >>> f({},d={},e={}) # ca marche aussi
        {5: 9}
        >>> f(a={},d={},e={}) # et non, a ne peut pas être passé par mot-clé
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        TypeError: f() got some positional-only arguments passed as keyword arguments: 'a'
        
        

        La partie l: Dict[int,int], c'est juste pour le sport. Ça déclare une variable annotée en tant que dictionnaire avec des clés de type entier et des valeurs de type entier. Pour rappel, les annotations sont ignorées par Python, il faut lancer un outil externe comme mypy pour en tirer quelque chose.

        La nouvelle syntaxe permettant du unpacking dans les dictionnaires: {5:9, **dict}, cela crée un nouveau dictionnaire, avec d'une part une clé 5 associée à la valeur 9, et toutes les autres clés-valeurs sont piochées dans le dictionnaire qui suit le **.

        Les dict comprehensions {v: v**2 for v in d }, cela crée un dictionnaire dont les clés seront v et associées aux valeur v**2 c'est à dire v puissance 2. La valeur v est piochée en parcourant les clés du dictionnaire d.

        On rajoute ensuite un filtre sur les valeurs qu'on pioche dans d via if v%2, en ne gardant que celles qui vérifient v%2 est vrai, c'est à dire v%2 différent de zéro, c'est à dire les nombres impairs. On a donc l'explication du { v: v**2 for v in d if v%2 }.

        Le (t:=v%2) permet de stocker la valeur v%2 dans une variable t, c'est la nouveauté du Python 3.8 expliquée dans la dépêche.

        En pratique:

        >>> t # variable does not exist yet
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
        NameError: name 't' is not defined
        >>> d={1:2, 2:3, 4:5}
        >>> l: Dict[int,int] = { 5:9, **{ v: v**2 for v in d if (t:=v%2) } }
        >>> t
        0
        >>> l
        {5: 9, 1: 1}
        
        • [^] # Re: Python se rapproche du Perl ?

          Posté par  (site web personnel) . Évalué à 3.

          Dans l'exemple de :=, ça serait mieux si t était réutilisé dans l'expression (à ce moment on ne calcule qu'une fois):

          >>> l: Dict[int,int] = { 5:9, **{ v: v%2 for v in d if v%2 } }
          >>> l: Dict[int,int] = { 5:9, **{ v: t for v in d if (t:=v%2) } }
          

          (note: pas testé, pas encore installé la version)

          Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

        • [^] # Re: Python se rapproche du Perl ?

          Posté par  (site web personnel) . Évalué à 1.

          J'ai compris plus ou moins le fonctionnement, mais pas l'intérêt. Quel est l'intérêt d'obliger à ce qu'un paramètre soit positionnel exclusivement?

          Un LUG en Lorraine : https://enunclic-cappel.fr

          • [^] # Re: Python se rapproche du Perl ?

            Posté par  (site web personnel) . Évalué à 5.

            C'est rarement utile, mais ça arrive que la sémantique des arguments change avec leur nombre et ordre. Par exemple la classique fonction range() ne prend pas d'arguments par mot-clé ; ça va pouvoir être formalisé plus proprement dans sa signature avec Python 3.8.

            C'est pratique aussi pour les IDE qui extraient un fichier .py représentant l'interface d'un module écrit en C dans lequel les fonctions utilisent uniquement l'ordre des arguments.

          • [^] # Commentaire supprimé

            Posté par  . Évalué à 3.

            Ce commentaire a été supprimé par l’équipe de modération.

    • [^] # Re: Python se rapproche du Perl ?

      Posté par  . Évalué à 2.

      Je me retrouve beaucoup dans ton commentaire.

      Chaque fois que je cherche comment faire quelque chose de "compliqué" en python, je tombe sur une question similaire à la mienne, et toujours un petit malin qui répond "à partir de la version 3.X.X, tu peux faire ça" suivi d'un code imbitable (donc impossible à adapter) comme ton exemple.

      THIS IS JUST A PLACEHOLDER. YOU SHOULD NEVER SEE THIS STRING.

  • # Arguments exclusivement positionnels ?

    Posté par  . Évalué à 3.

    Je ne comprend pas ce qui fait que def f(a, /, b): implique que a ne peut pas être nommé.

    Pour dire qu'un argument doit être exclusivement positionnel, faut-il le suffixer par , / ? Comme si on attendait un argument / ?

    Est-ce qu'on peut trouver de la documentation sur cette feature ?

    Je n'en ai pas trouvé après une brève recherche, mais j'imagine que c'est normal si c'est une nouvelle feature pas encore utilisée et discutée uniquement dans les ml de dev python.

    • [^] # Re: Arguments exclusivement positionnels ?

      Posté par  . Évalué à 5.

      Tous les arguments qui précèdent un "/" dans la liste des arguments sont exclusivement positionnels. C’est à dire si j’ai bien compris que leur valeur n’est accessible que par la variable locale ("a" ici) qui leur correspond.

      Si tu cherches un "a" comme clé du dictionnaire des arguments, sa valeur ne sera jamais celle du "a" positionnel, ce qui libère la clé "a" pour autre chose éventuellement.

      • [^] # Re: Arguments exclusivement positionnels ?

        Posté par  (site web personnel) . Évalué à 10.

        Ca permet aussi d'éviter ce problème :

        >>> def func(self, **kw): print(self, kw)
        ... 
        >>> func("obj", self=3)
        TypeError: func() got multiple values for argument 'self'
        

        Pas de soucis avec / :

        >>> def func(self, /, **kw): print(self, kw)
        ... 
        >>> func("obj", self=3)
        obj {'self': 3}
        
  • # Walrus

    Posté par  . Évalué à 2.

    C'est marrant, j'ignorais que le mot "Walrus" désignait autre chose que le navire du capitaine Flint.

    Merci pour la dépêche.

Suivre le flux des commentaires

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