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

79
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 (+14/-0).

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

    • [^] # Re: Merci

      Posté par (page perso) . Évalué à 10 (+16/-0).

      Héhé, merci d'avoir remarqué cela :-). Je trouve qu'on a trop tendance à faire des dépêches interminables qui ne sortent jamais. Ça finit même en série de dépêches !

      Pourtant, les débuts de DLFP étaient plus laconiques ! Façon slashdot.

      J'ai donc voulu faire un compromis par cette dépêche rapide avec quelques focus.

  • # Version de référence

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

    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.

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

      Posté par (page perso) . Évalué à 10 (+11/-0).

      Disons le franchement, parce que Python 3.6 est disponible pour RHEL7 (qui n'avait que Python 2.7 à la base) mais aussi pour RHEL8 !

      C'est aussi la version avec async/await et le typage. Sur ces sujets il y a plus de différence entre 3.5 et 3.6 qu'entre 3.6 et les suivante. Bref, il y a un avant et après 3.6.

      Admettons que tu souhaite écrire un projet libre, mais Python3 uniquement. La barrière de compatibilité la plus raisonnable actuellement me semble être 3.6. Donc les innovations syntaxiques de 3.8 ne devrait pas être trop répandue dans les bibliothèques souhaitant être portables.

      Pour les systèmes plus souples basés sur Debian et/ou Docker, ce n'est pas pareil. Si tu maintient une application interne et que tu maîtrise la version de Python utilisée, va pour la dernière !

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

  • # Coquille

    Posté par . Évalué à 0 (+1/-1). Dernière modification le 16/10/19 à 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 (page perso) . Évalué à 2 (+0/-1).

      Corrigé, merci.

    • [^] # Re: Coquille

      Posté par (page perso) . Évalué à 3 (+2/-1).

      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 (page perso) . Évalué à 3 (+2/-1).

        Corrigé merci.

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

        OS préféré Mageia 6 et Mageia 7, CMS préféré SPIP, suite bureautique préférée LibreOffice, logiciel de dessin préféré Inkscape.

    • [^] # Re: Coquille

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

      Autre petite coquille dans la section "Et plus" :

      Cela perme,t par exemple,

      Cela permet, par exemple.

      • [^] # Re: Coquille

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

        Corrigé merci.

        OS préféré Mageia 6 et Mageia 7, CMS préféré SPIP, suite bureautique préférée LibreOffice, logiciel de dessin préféré Inkscape.

  • # Yoda ?

    Posté par (page perso) . Évalué à 6 (+3/-0).

    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 ?

    • [^] # Re: Yoda ?

      Posté par (page perso) . Évalué à 10 (+8/-0).

      Bonne question, un lien vers Wikipedia aurait été utile. Voici un exemple de condition Yoda:

      if 'valeur' == mavar:
          ...

      Comme tu le vois, la valeur est positionnée avant la variable. En C, cela a l'avantage de générer une erreur de syntaxe si on oublie le deuxième =.

      if (mavar = 1)  // assigne 1 à mavar !!!!
      ...
      if (1 = mavar)  // ne compile pas \o/ !

      La notation Yoda est d'ailleurs utilisée dans les assert des tests unitaires :

      ...
      self.assertEquals('attendu', mavar)
      ...

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

      • [^] # Re: Yoda ?

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

        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 (+11/-0).

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

        • [^] # Re: Yoda ?

          Posté par (page perso) . Évalué à 5 (+3/-0).

          "valeur" mavar == ("À valeur ma variable est égale"), non ?

          Même plus je dirais : À valeur variable ma égale est ;-)

      • [^] # Re: Yoda ?

        Posté par (page perso) . Évalué à 3 (+0/-0).

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

        Oui, merci.

    • [^] # Re: Yoda ?

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

      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.

      • [^] # Re: Yoda ?

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

        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 (page perso) . Évalué à 7 (+5/-0).

          Ç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 (+1/-0). Dernière modification le 18/10/19 à 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 (page perso) . Évalué à 2 (+0/-0).

              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 (+2/-3).

          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/

          • [^] # Re: Yoda ?

            Posté par . Évalué à 4 (+4/-0). Dernière modification le 18/10/19 à 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 (page perso) . Évalué à 2 (+0/-0).

        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 (page perso) . Évalué à 1 (+1/-0).

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

        • [^] # Re: Yoda ?

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

          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.

          • [^] # Re: Yoda ?

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

            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 (+1/-0).

              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 ?

              • [^] # Re: Yoda ?

                Posté par (page perso) . Évalué à 2 (+0/-0). Dernière modification le 12/11/19 à 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.

  • # Python 3 a plus de versions que Python 2

    Posté par (page perso) . Évalué à 10 (+9/-0).

    À noter, avec cette version, Python 3 a plus de version majeure que Python 2. Ce n'est pas si anecdotique.

    • D'abord, cela insiste sur la maturité de Python 3. Elle vaut largement celle de Python 2.7. D'ailleurs, l'écosystème Python 3 est aujourd'hui très sain voir plus que pas mal de paquets python2 non maintenu.
    • Ensuite, et avec la 3.9 qui se défile, on voit bien la volonté de la PSF de prendre la voie de la compatibilité et de maintenir longtemps Python 3.
    • Quel chemin parcouru entre 3.0 et 3.8 ! aurait-on pensé aux nouveaux mots-clefs await/async, au typage et toutes ces nouvelles syntaxes lors de la 3.0 ?
  • # Python se rapproche du Perl ?

    Posté par (page perso) . Évalué à 10 (+38/-0).

    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 !

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

      Posté par (page perso) . Évalué à 10 (+13/-0).

      Tout à fait.

      C'est pourquoi perso, j'aurai préféré as plutôt que `:='. Ça me semble plus pythonique.

      if (re.match('motif', 'chaîne') as m) is not None:
          ...

      Bravo pour ton exemple de code illisible, c'est un bon condensé :D

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

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

        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.

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

          Posté par (page perso) . Évalué à 3 (+1/-0). Dernière modification le 16/10/19 à 14:51.

          C'est tout un tas de discussions. On trouve quelques références dans https://mail.python.org/pipermail/python-dev/2018-July/thread.html . Il doit y avoir aussi plusieurs discussions sur python-ideas.

          Peut-être que Victor STINNER ou Antoine PITROU pourrait nous éclairer. Ils passent par LinuxFR de temps en temps.

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

            Posté par (page perso) . Évalué à 9 (+7/-0).

            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 (+4/-0).

              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 (page perso) . Évalué à 4 (+2/-0). Dernière modification le 06/11/19 à 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 (+2/-0).

                  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 (+2/-0).

        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 (page perso) . Évalué à 7 (+5/-0).

      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 (page perso) . Évalué à 5 (+3/-0).

      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 (+2/-0).

      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 (+3/-0).

      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 (page perso) . Évalué à 10 (+9/-0).

        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 (page perso) . Évalué à 3 (+1/-0).

          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 en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

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

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

          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 (page perso) . Évalué à 5 (+3/-0).

            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.

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

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

            C'est le signe d'un langage mature. Les ajouts syntaxique ne révolutionnent pas le travail quotidiens des développeurs Python. On pouvait pas en dire autant d'async/await.

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

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

      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 (+2/-0).

    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 (+3/-0).

      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 (page perso) . Évalué à 10 (+8/-0).

        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 (+0/-0).

    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.

Envoyer un commentaire

Suivre le flux des commentaires

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