4 fonctionnalités puissantes Python manque toujours

Python est un langage vivant - en développement constant pour suivre le rythme. La Python Software Foundation ne fait pas seulement des ajouts à la bibliothèque standard et à l'implémentation de référence CPython, mais introduit également de nouvelles fonctionnalités et des améliorations dans le langage lui-même.

Par exemple, Python 3.8 a introduit une nouvelle syntaxe pour les affectations en ligne («l'opérateur morse») qui rend certaines opérations plus concises. Une autre amélioration de la syntaxe récemment approuvée, la correspondance de modèles, facilitera l'écriture de code qui évalue l'un des nombreux cas possibles. Ces deux fonctionnalités ont été inspirées par leur présence et leur utilité dans d'autres langues.

Et ce ne sont que deux des nombreuses fonctionnalités utiles qui pourraient être ajoutées à Python pour rendre le langage plus expressif, plus puissant, plus adapté au monde de la programmation moderne. Que pourrions-nous souhaiter d'autre? Voici quatre autres fonctionnalités de langage qui pourraient ajouter quelque chose de réelle valeur à Python - deux que nous pourrions réellement obtenir, et deux nous ne le ferons probablement pas. 

Vraies constantes

Python n'a pas vraiment le concept de valeur constante. Aujourd'hui, les constantes en Python sont principalement une question de convention. Utiliser un nom en majuscules et en cas de serpent - par exemple, DO_NOT_RESTART - indique que la variable est censée être une constante. De même, l'  typing.Final annotation de type fournit un indice aux linters indiquant qu'un objet ne doit pas être modifié, mais elle ne l'applique pas lors de l'exécution.

Pourquoi? Parce que la mutabilité est profondément ancrée dans les comportements de Python. Lorsque vous affectez une valeur à une variable - par exemple,  x=3 - vous créez un nom dans l'espace de noms local  x, et vous le pointez sur un objet du système qui a la valeur entière  3. Python suppose à tout moment que les noms sont modifiables - que n'importe quel nom peut pointer vers n'importe quel objet. Cela signifie qu'à chaque fois qu'un nom est utilisé, Python se donne la peine de rechercher l'objet sur lequel il pointe. Ce dynamisme est l'une des principales raisons pour lesquelles Python fonctionne plus lentement que certains autres langages. Le dynamisme de Python offre une grande flexibilité et commodité, mais cela se fait au détriment des performances d'exécution.

Un avantage d'avoir de vraies déclarations constantes en Python serait une certaine réduction de la fréquence des recherches d'objets qui ont lieu pendant l'exécution, et donc de meilleures performances. Si le moteur d'exécution sait à l'avance qu'une valeur donnée ne change jamais, il n'a pas besoin de rechercher ses liaisons. Cela pourrait également fournir une avenue pour d'autres optimisations tierces, comme des systèmes qui génèrent du code natif machine à partir d'applications Python (Cython, Nuitka).

Cependant, les vraies constantes constitueraient un changement majeur, et très probablement un changement rétrocompatible. Il serait également à débattre si les constantes venaient au moyen d'une nouvelle syntaxe - par exemple, le $ symbole encore inutilisé  - ou comme une extension de la manière existante de Python de déclarer les noms. Enfin, il y a la question philosophique plus large de savoir si les vraies constantes ont un sens dans une langue où le dynamisme a été une grande partie de l'attrait.

En bref, il est possible que nous voyions de vraies constantes en Python, mais ce serait un changement majeur.

Véritable surcharge et génériques

Dans de nombreuses langues, plusieurs versions de la même fonction peuvent être écrites pour fonctionner avec différents types d'entrée. Par exemple, une  to_string() fonction pourrait avoir différentes implémentations pour la conversion à partir d'entiers, de nombres à virgule flottante ou d'autres objets - mais ils partageraient le même nom pour des raisons de commodité. La «surcharge», ou «génériques», facilite l'écriture de logiciels robustes, car vous pouvez écrire des méthodes génériques pour des processus courants plutôt que d'utiliser une méthode spécifiquement pour un type donné.

Python vous permet d'utiliser un seul nom de fonction pour faire le travail de plusieurs, mais pas en définissant plusieurs instances d'une fonction. Vous ne pouvez définir un nom qu'une seule fois dans une portée donnée et le lier à un seul objet à la fois, vous ne pouvez donc pas avoir plusieurs versions d'une même fonction sous le même nom.

Ce que les développeurs Python font généralement pour contourner ce problème, c'est utiliser des éléments intégrés tels que  isinstance() ou  type() pour déterminer le type de variable soumis à une fonction, puis agir en fonction du type. Parfois, cela implique l'envoi à une version spécifique au type d'une fonction sous le capot. Mais cette approche rend difficile pour les autres développeurs d'étendre votre fonction à moins que vous ne fassiez tout votre possible pour la rendre extensible - par exemple, en distribuant des méthodes au sein d'une classe, qui pourrait être sous-classée.

La PEP 3124, avancée en avril 2007, proposait un mécanisme de décoration des fonctions pour indiquer qu'elles pourraient être surchargées. La proposition a été reportée plutôt que rejetée carrément - ce qui signifie que l'idée était fondamentalement saine, mais le moment n'était pas propice pour la mettre en œuvre. Un facteur qui pourrait accélérer l'adoption de la surcharge en Python - ou entraîner l'abandon complet de l'idée - est la mise en œuvre du système de correspondance de modèles nouvellement proposé.

En théorie, la correspondance de modèle pourrait être utilisée sous le capot pour gérer l'envoi de surcharge. Cependant, la correspondance de modèles pourrait également être donnée comme une justification pour ne pas implémenter les génériques en Python, car elle fournit déjà un moyen élégant de distribuer des opérations basées sur des signatures de type.

Nous pourrions donc avoir un jour une véritable surcharge en Python, ou ses avantages pourraient être remplacés par d'autres mécanismes.

Optimisations de la récursivité de la queue

De nombreux compilateurs de langage utilisent des optimisations de récursivité de queue, où les fonctions qui s'appellent elles-mêmes ne créent pas de nouveaux cadres de pile dans l'application, et risquent donc de faire exploser la pile si elles s'exécutent trop longtemps. Python ne fait pas cela, et en fait, ses créateurs se sont toujours opposés à le faire.

Une des raisons est qu'une grande partie de Python, de l'intérieur vers l'extérieur, utilise l'  itération  plutôt que la  récursivité  - générateurs, coroutines, etc. Dans ce cas, cela signifie utiliser une fonction avec une boucle et une structure de pile au lieu d'un mécanisme récursif. Chaque appel de la boucle peut être enregistré dans une pile pour créer une nouvelle récursivité, et sorti de la pile lorsque la récursivité se termine.

Les développeurs Python sont encouragés à utiliser ces modèles au lieu de la récursivité, il semble donc qu'il y ait peu d'espoir pour des optimisations de récursivité. Les chances ici sont peu probables, car les idiomes de Python prennent en charge d'autres solutions.

Lambdas multilignes

Les lambdas, ou fonctions anonymes, ne sont entrées en Python qu'après une certaine résistance de la part du créateur du langage Guido van Rossum. Comme les lambdas Python existent maintenant, ils sont très contraints: ils ne vous permettent d'utiliser qu'une seule expression (essentiellement, tout ce qui se trouve à droite d'un signe égal dans une opération d'affectation) comme corps de fonction. Si vous voulez un bloc complet d'instructions, il suffit de les séparer et d'en faire une fonction réelle.

La raison se résume à la conception du langage tel que le voit van Rossum. Comme l'écrivait van Rossum en 2006, «je trouve   inacceptable toute solution qui intègre un bloc basé sur l'indentation au milieu d'une expression. Étant donné que je trouve également inacceptable une syntaxe alternative pour le regroupement d'instructions (par exemple, des accolades ou des mots-clés de début / fin), cela fait à peu près d'un lambda multiligne un casse-tête insoluble. "

En d'autres termes, le problème n'est pas technique, mais l'absence d'une syntaxe pour les lambdas multilignes qui complète l'esthétique existante de la syntaxe Python. Il n'y a probablement aucun moyen de le faire qui n'implique pas de créer un cas particulier, et un langage qui génère des cas spéciaux a tendance à devenir désagréable à utiliser. Jusqu'à ce qu'une telle licorne apparaisse, nous devrons simplement nous contenter de fonctions définies séparément.

Les lambdas multilignes ne se produisent probablement pas en Python.

En savoir plus sur Python:

  • Python 3.9: Quoi de neuf et de meilleur
  • Les meilleures nouvelles fonctionnalités de Python 3.8
  • Meilleure gestion de projet Python avec Poetry
  • Virtualenv et venv: les environnements virtuels Python expliqués
  • Python Virtualenv et Venv à faire et à ne pas faire
  • Explication des threads et sous-processus Python
  • Comment utiliser le débogueur Python
  • Comment utiliser timeit pour profiler le code Python
  • Comment utiliser cProfile pour profiler le code Python
  • Démarrez avec async en Python
  • Comment utiliser asyncio en Python
  • Comment convertir Python en JavaScript (et inversement)
  • Python 2 EOL: Comment survivre à la fin de Python 2
  • 12 pythons pour chaque besoin de programmation
  • 24 bibliothèques Python pour chaque développeur Python
  • 7 IDE Python que vous avez peut-être manqués
  • 3 lacunes majeures de Python et leurs solutions
  • 13 frameworks Web Python comparés
  • 4 frameworks de test Python pour écraser vos bugs
  • 6 nouvelles fonctionnalités Python à ne pas manquer
  • 5 distributions Python pour maîtriser l'apprentissage automatique
  • 8 grandes bibliothèques Python pour le traitement du langage naturel
  • 6 bibliothèques Python pour le traitement parallèle
  • Qu'est-ce que PyPy? Python plus rapide sans douleur
  • Qu'est-ce que Cython? Python à la vitesse de C