Nouvelles fonctionnalités intéressantes dans Python 3.8 – Real Python

By | octobre 14, 2019

Python pas cher

La nouvelle version de Python est prévue pour la publication aujourd'hui! Python 3.8 est disponible en version bêta depuis l'été, mais le 14 octobre 2019, la première version officielle est prête. Aujourd'hui, nous pouvons tous commencer à jouer avec les nouvelles fonctionnalités et bénéficier des dernières améliorations.

Qu'est-ce que Python 3.8 apporte à la table? La documentation donne un bon aperçu des nouvelles fonctionnalités. Cependant, cet article approfondira certains des changements les plus importants et vous montrera comment tirer parti de Python 3.8.

Dans cet article, vous apprendrez:

  • Utilisation d'expressions d'affectation pour simplifier certaines constructions de code
  • Appliquer des arguments de position uniquement dans vos propres fonctions
  • Spécification d'indications de type plus précises
  • Utilisation de f-strings pour un débogage plus simple

À quelques exceptions près, Python 3.8 contient de nombreuses petites améliorations par rapport aux versions précédentes. Vers la fin de l'article, vous verrez beaucoup de ces changements moins intéressants, ainsi qu'une discussion sur certaines des optimisations qui rendent Python 3.8 plus rapide que ses prédécesseurs. Enfin, vous obtiendrez des conseils sur la mise à niveau vers la nouvelle version.

Le morse dans la pièce: expressions d'assignation

Le plus gros changement de Python 3.8 est l’introduction de expressions d'affectation. Ils sont écrits en utilisant une nouvelle notation (: =). Cet opérateur est souvent appelé le opérateur de morse comme il ressemble aux yeux et aux défenses d'un morse sur le côté.

Les expressions d'assignation vous permettent d'assigner et de renvoyer une valeur dans la même expression. Par exemple, si vous souhaitez affecter une variable et imprimer sa valeur, procédez comme suit:

>>>

>>> morse = Faux
>>> impression(morse)
Faux

Dans Python 3.8, vous êtes autorisé à combiner ces deux instructions en une seule, à l'aide de l'opérateur de morse:

>>>

>>> impression(morse := Vrai)
Vrai

L'expression d'affectation vous permet d'affecter Vrai à morse, et affiche immédiatement la valeur. Mais gardez à l’esprit que l’opérateur de morse ne ne pas faire tout ce qui n’est pas possible sans cela. Cela rend seulement certaines constructions plus pratiques et peut parfois communiquer plus clairement l'intention de votre code.

Un modèle qui montre certaines des forces de l'opérateur de morse est tandis que boucles où vous devez initialiser et mettre à jour une variable. Par exemple, le code suivant demande à l’utilisateur une entrée jusqu’à ce qu’il tape quitter:

contributions = liste()
courant = contribution("Écris quelque chose: ")
tandis que courant ! = "quitter":
    contributions.ajouter(courant)
    courant = contribution("Écris quelque chose: ")

Ce code est moins qu'idéal. Vous répétez le contribution() déclaration, et en quelque sorte, vous devez ajouter courant à la liste avant demander à l'utilisateur pour cela. Une meilleure solution consiste à mettre en place une infinie tandis que boucle, et utiliser Pause pour arrêter la boucle:

contributions = liste()
tandis que Vrai:
    courant = contribution("Écris quelque chose: ")
    si courant == "quitter":
        Pause
    contributions.ajouter(courant)

Ce code est équivalent à celui ci-dessus, mais évite la répétition et garde en quelque sorte les lignes dans un ordre plus logique. Si vous utilisez une expression d'affectation, vous pouvez simplifier davantage cette boucle:

contributions = liste()
tandis que (courant := contribution("Écris quelque chose: ")) ! = "quitter":
    contributions.ajouter(courant)

Ceci ramène le test à la tandis que ligne, où il devrait être. Cependant, plusieurs choses se passent maintenant sur cette ligne, il faut donc un peu plus d'efforts pour la lire correctement. Utilisez votre meilleur jugement pour savoir quand l'opérateur de morse aide à rendre votre code plus lisible.

PEP 572 décrit tous les détails des expressions d’affectation, y compris une partie de la justification de leur introduction dans le langage, ainsi que plusieurs exemples de la manière dont l’opérateur de morse peut être utilisé.

Arguments de type positionnel

La fonction intégrée flotte() peut être utilisé pour convertir des chaînes de texte et des nombres en flotte objets. Prenons l'exemple suivant:

>>>

>>> flotte("3.8")
3.8

>>> Aidez-moi(flotte)
classe float (objet)
    | float (x = 0, /)
    |  
    | Convertissez une chaîne ou un nombre en nombre à virgule flottante, si possible.

[...]

Regardez attentivement la signature de flotte(). Remarquez la barre oblique (/) après le paramètre. Qu'est-ce que ça veut dire?

Il s’avère que si le seul paramètre de flotte() est appelé X, vous n'êtes pas autorisé à utiliser son nom:

>>>

>>> flotte(X="3.8")
Traceback (dernier appel le plus récent):
  Fichier "", ligne 1, dans 
Erreur-type: float () ne prend pas d'argument mot clé

Lors de l'utilisation flotte() vous êtes uniquement autorisé à spécifier des arguments par position, pas par mot-clé. Avant Python 3.8, tel que positionnel seulement les arguments n'étaient possibles que pour les fonctions intégrées. Il n'y avait pas de moyen facile de spécifier que les arguments devraient être positionnels uniquement dans vos propres fonctions:

>>>

>>> def incr(X):
...     revenir X + 1
... 
>>> incr(3.8)
4.8

>>> incr(X=3.8)
4.8

Il est possible de simuler des arguments de position seulement en utilisant * args, mais cela est moins flexible, moins lisible et vous oblige à implémenter votre propre analyse d’argument. En Python 3.8, vous pouvez utiliser / pour indiquer que tous les arguments précédents doivent être spécifiés par position. Vous pouvez réécrire incr () pour n'accepter que des arguments de position:

>>>

>>> def incr(X, /):
...     revenir X + 1
... 
>>> incr(3.8)
4.8

>>> incr(X=3.8)
Traceback (dernier appel le plus récent):
  Fichier "", ligne 1, dans 
Erreur-type: incr () a eu quelques arguments positionnels seulement passés comme
                                            arguments de mots clés: 'x'

En ajoutant / après X, vous spécifiez que X est un argument de position seulement. Vous pouvez combiner des arguments normaux avec des arguments positionnels uniquement en plaçant les arguments normaux après la barre oblique:

>>>

>>> def saluer(Nom, /, salutation="Salut"):
...     revenir F"salutation, Nom"
... 
>>> saluer("Łukasz")
'Bonjour, Łukasz'

>>> saluer("Łukasz", salutation="Emploi super")
'Excellent travail, Łukasz'

>>> saluer(Nom="Łukasz", salutation="Emploi super")
Traceback (dernier appel le plus récent):
  Fichier "", ligne 1, dans 
Erreur-type: greet () a eu quelques arguments positionnels seulement passés comme
                                            arguments de mots clés: 'nom'

Dans saluer(), le slash est placé entre Nom et salutation. Cela signifie que Nom est un argument de position seulement, alors que salutation est un argument régulier qui peut être passé par position ou par mot clé.

À première vue, les arguments de position uniquement peuvent sembler un peu limitatifs et contraires au mantra de Python concernant l’importance de la lisibilité. Vous constaterez probablement qu'il n'y a pas beaucoup d'occasions où des arguments de position uniquement améliorent votre code.

Toutefois, dans les bonnes circonstances, les arguments de position uniquement peuvent vous donner une certaine souplesse lors de la conception de fonctions. Premièrement, les arguments uniquement positionnels ont du sens lorsque vous avez des arguments qui ont un ordre naturel mais sont difficiles à donner à de bons noms descriptifs.

Un autre avantage possible d'utiliser des arguments uniquement positionnels est que vous pouvez plus facilement refactoriser vos fonctions. En particulier, vous pouvez modifier le nom de vos paramètres sans vous soucier du fait que l'autre code dépend de ces noms.

Les arguments positionnels seulement complètent bien mot-clé seulement arguments. Dans toute version de Python 3, vous pouvez spécifier des arguments composés uniquement de mots clés à l'aide de l'étoile (*). N'importe quel argument après * doit être spécifié en utilisant un mot clé:

>>>

>>> def to_fahrenheit(*, celsius):
...     revenir 32 + celsius * 9 / 5
... 
>>> to_fahrenheit(40)
Traceback (dernier appel le plus récent):
  Fichier "", ligne 1, dans 
Erreur-type: to_fahrenheit () prend 0 argument de position mais 1 a été donné

>>> to_fahrenheit(celsius=40)
104,0

celsius est un argument composé uniquement de mots clés. Python génère donc une erreur si vous essayez de le spécifier en fonction de la position, sans le mot clé.

Vous pouvez combiner des arguments de type positional, standard et mot-clé uniquement, en les spécifiant dans cet ordre, séparés par / et *. Dans l'exemple suivant, texte est un argument de position seulement, frontière est un argument régulier avec une valeur par défaut, et largeur est un argument composé uniquement de mots clés avec une valeur par défaut:

>>>

>>> def gros titre(texte, /, frontière="♦", *, largeur=50):
...     revenir F" texte    ".centre(largeur, frontière)
... 

Puisque texte est uniquement positionnel, vous ne pouvez pas utiliser le mot clé texte:

>>>

>>> gros titre("Arguments de type positionnel")
'♦_reva_____________________________________________________________________________________________________________________________________________________________________________' '

>>> gros titre(texte="Ça ne marche pas!")
Traceback (dernier appel le plus récent):
  Fichier "", ligne 1, dans 
Erreur-type: headline () a eu quelques arguments positionnels seulement passés en tant que
                                            arguments de mots clés: 'text'

frontière, d'autre part, peuvent être spécifiés avec et sans le mot clé:

>>>

>>> gros titre("Python 3.8", "=")
'=================== Python 3.8 =================='

>>> gros titre("Vrai python", frontière=":")
':::::::::::::::::: Python réel :::::::::::::::::' '

Finalement, largeur doit être spécifié en utilisant le mot-clé:

>>>

>>> gros titre("Python", "", largeur=38)
'🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍🐍 Python'

>>> gros titre("Python", "", 38)
Traceback (dernier appel le plus récent):
  Fichier "", ligne 1, dans 
Erreur-type: headline () prend de 1 à 2 arguments de position
                                            mais 3 ont été donnés

Vous pouvez en savoir plus sur les arguments positionnels uniquement dans PEP 570.

Des types plus précis

Le système de dactylographie de Python est assez mature à ce stade. Cependant, dans Python 3.8, de nouvelles fonctionnalités ont été ajoutées à dactylographie pour permettre une frappe plus précise:

  • Types littéraux
  • Dictionnaires dactylographiés
  • Objets finaux
  • Protocoles

Python prend en charge facultatif indications de type, typiquement sous forme d’annotations sur votre code:

def double(nombre: flotte) -> flotte:
    revenir 2 * nombre

Dans cet exemple, vous dites que nombre devrait être un flotte et le double() la fonction devrait retourner un flotte, ainsi que. Cependant, Python traite ces annotations comme conseils. Ils ne sont pas appliqués à l'exécution:

>>>

>>> double(3.14)
6,28

>>> double("Je ne suis pas un char")
"Je ne suis pas un char Je ne suis pas un char"

double() accepte avec joie "Je ne suis pas un char" comme argument, même si ce n’est pas un flotte. Certaines bibliothèques peuvent utiliser des types au moment de l’exécution, mais ce n’est pas le principal cas d’utilisation du système de types de Python.

Au lieu de cela, les astuces de type permettent aux vérificateurs de type statiques de vérifier votre code Python, sans exécuter vos scripts. Cela rappelle que les compilateurs attrapent les erreurs de type dans d'autres langages tels que Java, Rust et Crystal. De plus, les repères de type servent de documentation de votre code, facilitant ainsi sa lecture et améliorant l'auto-complétion dans votre IDE.

Vous trouverez plus d'informations sur les indicateurs de type en Python dans le PEP 484 d'origine, ainsi que dans la vérification de type Python (Guide).

Quatre nouveaux PEP concernant la vérification de type ont été acceptés et inclus dans Python 3.8. Vous verrez de courts exemples dans chacun de ces domaines.

PEP 586 présente le Littéral type. Littéral est un peu spécial dans le sens où il représente une ou plusieurs valeurs spécifiques. Un cas d'utilisation de Littéral doit pouvoir ajouter avec précision des types, lorsque des arguments de chaîne sont utilisés pour décrire un comportement spécifique. Prenons l'exemple suivant:

# draw_line.py

def dessiner une ligne(direction: str) -> Aucun:
    si direction == "horizontal":
        ...  # Trace une ligne horizontale

    elif direction == "verticale":
        ...  # Tracer une ligne verticale

    autre:
        élever ValueError(F"direction invalide direction! r")

dessiner une ligne("up")

Le programme passera le vérificateur de type statique, même si "up" est une direction invalide. Le vérificateur de type vérifie uniquement que "up" est une ficelle. Dans ce cas, il serait plus précis de dire que direction doit être soit la chaîne littérale "horizontal" ou la chaîne littérale "verticale". En utilisant Littéral, vous pouvez faire exactement cela:

# draw_line.py

de dactylographie importation Littéral

def dessiner une ligne(direction: Littéral[[[["horizontal", "verticale"]) -> Aucun:
    si direction == "horizontal":
        ...  # Trace une ligne horizontale

    elif direction == "verticale":
        ...  # Tracer une ligne verticale

    autre:
        élever ValueError(F"direction invalide direction! r")

dessiner une ligne("up")

En exposant les valeurs autorisées de direction au contrôleur de type, vous pouvez maintenant être averti de l'erreur:

$ mypy draw_line.py 
draw_line.py:15: erreur:
                L'argument 1 à "draw_line" a un type incompatible "Literal['up']";
                attendu "Union[Literal['horizontal'], Littéral['vertical']]"
Trouvé 1 erreur dans 1 fichier (vérifié 1 fichier source)

La syntaxe de base est Littéral[[[[]. Par exemple, Littéral[38] représente la valeur littérale 38. Vous pouvez exprimer une ou plusieurs valeurs littérales à l'aide de syndicat:

syndicat[[[[Littéral[[[["horizontal"], Littéral[[[["verticale"]]

Comme il s’agit d’un cas d’utilisation assez courant, vous pouvez (et devriez probablement) utiliser la notation plus simple. Littéral["horizontal", "vertical"] au lieu. Vous avez déjà utilisé ce dernier lors de l'ajout de types à dessiner une ligne(). Si vous regardez attentivement la sortie de Mypy ci-dessus, vous pouvez voir qu’elle traduit la notation plus simple en syndicat notation en interne.

Il existe des cas où le type de la valeur de retour d'une fonction dépend des arguments d'entrée. Un exemple est ouvert() qui peut renvoyer une chaîne de texte ou un tableau d'octets en fonction de la valeur de mode. Cela peut être géré par la surcharge.

L’exemple suivant montre le squelette d’une calculatrice pouvant renvoyer la réponse sous forme de nombres normaux (38), ou en chiffres romains (XXXVIII):

# calculator.py

de dactylographie importation syndicat

ARABIC_TO_ROMAN = [([([([(1000, "M"), (900, "CM"), (500, "RÉ"), (400, "CD"),
                   (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
                   (dix, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "JE")]

def _convert_to_roman_numeral(nombre: int) -> str:
    "" "Convertir le nombre en chaîne de chiffres romains" ""
    résultat = liste()
    pour arabe, romain dans ARABIC_TO_ROMAN:
        compter, nombre = divmod(nombre, arabe)
        résultat.ajouter(romain * compter)
    revenir "".joindre(résultat)

def ajouter(num_1: int, num_2: int, to_roman: bool = Vrai) -> syndicat[[[[str, int]:
    "" "Ajouter deux nombres" ""
    résultat = num_1 + num_2

    si to_roman:
        revenir _convert_to_roman_numeral(résultat)
    autre:
        revenir résultat

Le code a les indications de type correctes: le résultat de ajouter() sera soit str ou int. Cependant, souvent ce code sera appelé avec un littéral Vrai ou Faux comme la valeur de to_roman auquel cas vous voudriez que le vérificateur de type en déduise exactement si str ou int est retourné. Cela peut être fait en utilisant Littéral ensemble avec @surcharge:

# calculator.py

de dactylographie importation Littéral, surcharge, syndicat

ARABIC_TO_ROMAN = [([([([(1000, "M"), (900, "CM"), (500, "RÉ"), (400, "CD"),
                   (100, "C"), (90, "XC"), (50, "L"), (40, "XL"),
                   (dix, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "JE")]

def _convert_to_roman_numeral(nombre: int) -> str:
    "" "Convertir le nombre en chaîne de chiffres romains" ""
    résultat = liste()
    pour arabe, romain dans ARABIC_TO_ROMAN:
        compter, nombre = divmod(nombre, arabe)
        résultat.ajouter(romain * compter)
    revenir "".joindre(résultat)

@surcharge
def ajouter(num_1: int, num_2: int, to_roman: Littéral[[[[Vrai]) -> str: ...
@surcharge
def ajouter(num_1: int, num_2: int, to_roman: Littéral[[[[Faux]) -> int: ...

def ajouter(num_1: int, num_2: int, to_roman: bool = Vrai) -> syndicat[[[[str, int]:
    "" "Ajouter deux nombres" ""
    résultat = num_1 + num_2

    si to_roman:
        revenir _convert_to_roman_numeral(résultat)
    autre:
        revenir résultat

L'ajouté @surcharge les signatures aideront votre vérificateur de type à déduire str ou int en fonction des valeurs littérales de to_roman. Notez que les ellipses (...) font littéralement partie du code. Ils remplacent le corps de la fonction dans les signatures surchargées.

En complément de Littéral, PEP 591 introduit Final. Ce qualificatif spécifie qu'une variable ou un attribut ne doit pas être réaffecté, redéfini ou remplacé. Ce qui suit est une erreur de frappe:

de dactylographie importation Final

ID: Final = 1

...

ID + = 1

Mypy mettra en évidence la ligne ID + = 1et notez que vous Impossible d'affecter au nom final "ID". Cela vous permet de vous assurer que les constantes de votre code ne changent jamais de valeur.

En outre, il existe également un @final décorateur qui peut être appliqué à des classes et des méthodes. Cours décorés avec @final ne peut pas être sous-classé, alors que @final Les méthodes ne peuvent pas être remplacées par des sous-classes:

de dactylographie importation final

@final
classe Base:
    ...

classe Sous(Base):
    ...

Mypy signalera cet exemple avec le message d'erreur Impossible d'hériter de la classe finale "Base". En apprendre davantage sur Final et @final, voir PEP 591.

Le troisième PEP permettant des indications de type plus spécifiques est PEP 589, qui introduit TypedDict. Ceci peut être utilisé pour spécifier des types de clés et de valeurs dans un dictionnaire en utilisant une notation similaire à la saisie. NamedTuple.

Traditionnellement, les dictionnaires étaient annotés en utilisant Dict. Le problème est que cela n’autorisait qu’un seul type pour les clés et un type pour les valeurs, ce qui conduisait souvent à des annotations telles que Dict[str, Any]. A titre d'exemple, considérons un dictionnaire qui enregistre des informations sur les versions de Python:

py38 = "version": "3.8", "année de sortie": 2019

La valeur correspondant à version est une chaîne, alors que année de sortie est un entier. Cela ne peut pas être représenté précisément avec Dict. Avec le nouveau TypedDict, vous pouvez faire ce qui suit:

de dactylographie importation TypedDict

classe PythonVersion(TypedDict):
    version: str
    année de sortie: int

py38 = PythonVersion(version="3.8", année de sortie=2019)

Le vérificateur de type pourra alors en déduire que py38["version"] a le type str, tandis que py38["release_year"] est un int. Au moment de l'exécution, un TypedDict est un habitué dict, et les indications de type sont ignorées comme d’habitude. Vous pouvez aussi utiliser TypedDict purement comme une annotation:

py38: PythonVersion = "version": "3.8", "année de sortie": 2019

Mypy vous indiquera si l'une de vos valeurs a un type incorrect ou si vous utilisez une clé qui n'a pas été déclarée. Voir PEP 589 pour plus d'exemples.

Mypy a soutenu Protocoles depuis un moment déjà. Cependant, l'acceptation officielle n'a eu lieu qu'en mai 2019.

Les protocoles sont un moyen de formaliser le support de Python pour le typage de canard:

Quand je vois un oiseau qui marche comme un canard et qui nage comme un canard et qui charlote comme un canard, j'appelle cet oiseau un canard. (La source)

La saisie de canard vous permet, par exemple, de lire .Nom sur tout objet qui a un .Nom attribut, sans vraiment se soucier du type de l’objet. Cela peut sembler contre-intuitif pour le système de frappe de le supporter. Grâce au sous-typage structurel, il est encore possible de donner un sens au dactylographie du canard.

Vous pouvez par exemple définir un protocole appelé Nommé qui peut identifier tous les objets avec un .Nom attribut:

de dactylographie importation Protocole

classe Nommé(Protocole):
    Nom: str

def saluer(obj: Nommé) -> Aucun:
    impression(F"Salut obj.name")

Ici, saluer() prend n'importe quel objet, tant qu'il définit une .Nom attribut. Voir PEP 544 et la documentation Mypy pour plus d'informations sur les protocoles.

Débogage plus simple avec f-Strings

Les cordes f ont été introduites dans Python 3.6 et sont devenues très populaires. C’est peut-être la raison la plus courante pour laquelle les bibliothèques Python ne sont prises en charge que par la version 3.6 et ultérieure. Une chaîne de caractères est un littéral de chaîne formaté. Vous pouvez le reconnaître par le leader F:

>>>

>>> style = "formaté"
>>> F"C'est un style    chaîne"
'Ceci est une chaîne formatée'

Lorsque vous utilisez des chaînes de caractères, vous pouvez inclure des variables et même des expressions entre accolades. Ils seront ensuite évalués au moment de l'exécution et inclus dans la chaîne. Vous pouvez avoir plusieurs expressions dans une chaîne f:

>>>

>>> importation math
>>> r = 3.6

>>> F"Un cercle de rayon r    a l'aire math.pi * r * r: .2f "
'Un cercle de rayon 3.6 a une surface de 40,72'

Dans la dernière expression, math.pi * r * r: .2f, vous utilisez également un spécificateur de format. Les spécificateurs de format sont séparés des expressions par deux points.

.2f signifie que la zone est formatée comme un nombre à virgule flottante avec 2 décimales. Les spécificateurs de format sont les mêmes que pour .format(). Voir la documentation officielle pour une liste complète des spécificateurs de format autorisés.

Dans Python 3.8, vous pouvez utiliser des expressions d’affectation dans des chaînes de caractères. Veillez simplement à entourer l'expression d'affectation de parenthèses:

>>>

>>> importation math
>>> r = 3.8

>>> F"Diamètre (diam: = 2 * r) donne la circonférence math.pi * diam: .2f"
'Diamètre 7.6 donne circonférence 23.88'

Cependant, la vraie nouvelle de f-news dans Python 3.8 est le nouveau spécificateur de débogage. Vous pouvez maintenant ajouter = à la fin d'une expression, et cela affichera à la fois l'expression et sa valeur:

>>>

>>> python = 3.8
>>> F"python ="
'python = 3.8'

C'est un raccourci, qui sera généralement plus utile lorsque vous travaillez de manière interactive ou que vous ajoutez des instructions d'impression pour déboguer votre script. Dans les versions antérieures de Python, vous deviez épeler la variable ou l'expression deux fois pour obtenir les mêmes informations:

>>>

>>> python = 3.7
>>> F"python =python"
'python = 3.7'

Vous pouvez ajouter des espaces autour =, et utilise les spécificateurs de format comme d'habitude:

>>>

>>> Nom = "Eric"
>>> F"name ="
"name = 'Eric'"

>>> F"name =:> 10"
'nom = Eric'

le > 10 spécificateur de format dit que Nom doit être aligné à droite dans une chaîne de 10 caractères. = fonctionne également pour les expressions plus complexes:

>>>

>>> F"name.upper ()[::-1] = "
"name.upper ()[::-1] = 'CIRE' "

Pour plus d’informations sur les chaînes de caractères, consultez f-Strings de Python 3: Syntaxe de formatage de chaînes améliorée (Guide).

Le conseil de direction de Python

Techniquement, Python la gouvernance n'est pas une fonctionnalité linguistique. Cependant, Python 3.8 est la première version de Python non développée sous la version précédente. dictature bienveillante de Guido van Rossum. Le langage Python est maintenant régi par un conseil de direction composé de cinq développeurs principaux:

La voie vers le nouveau modèle de gouvernance pour Python était une étude intéressante sur l’auto-organisation. Guido van Rossum a créé Python au début des années 1990 et a été surnommé affectueusement par Python. Dictateur bienveillant pour la vie (BDFL). Au fil des ans, de plus en plus de décisions concernant le langage Python ont été prises via Propositions d'amélioration Python (PPE). Malgré tout, Guido avait officiellement le dernier mot pour toute nouvelle fonctionnalité linguistique.

Après une longue et longue discussion sur les expressions d'affectation, Guido a annoncé en juillet 2018 qu'il se retirait de son rôle de BDFL (pour de vrai cette fois). À dessein, il n'a pas nommé de successeur. Au lieu de cela, il a demandé à l'équipe de développeurs principaux de déterminer comment Python devrait être gouverné à l'avenir.

Heureusement, le processus de PPE était déjà bien établi. Il était donc naturel d’utiliser les PPE pour discuter et décider d’un nouveau modèle de gouvernance. À l'automne 2018, plusieurs modèles ont été proposés, y compris l'élection d'un nouveau BDFL (rebaptisé «Agent des décisions d'influence de l'arbitre gracieux: le GUIDO)» ou le passage à un modèle basé sur le consensus et le vote, sans direction centralisée. En décembre 2018, le modèle de conseil de direction a été choisi après un vote parmi les développeurs principaux.

Le comité de pilotage Python à la conférence PyCon 2019
Le comité directeur de Python à la conférence Pycon 2019. De gauche à droite: Barry Varsovie, Brett Cannon, Carol Willing, Guido van Rossum et Nick Loghlan (illustration: Geir Arne Hjelle)

Le comité directeur se compose de cinq membres de la communauté Python, énumérés ci-dessus. Il y aura une élection pour un nouveau conseil de direction après chaque version majeure de Python. En d'autres termes, il y aura une élection après la publication de Python 3.8.

Bien qu’il s’agisse d’une élection ouverte au public, on s’attend à ce que la plupart des membres du conseil directeur inaugural, sinon tous, soient réélus. Le conseil de direction dispose de pouvoirs étendus pour prendre des décisions concernant le langage Python, mais doit s’efforcer de les exercer le moins possible.

Vous pouvez lire tout sur le nouveau modèle de gouvernance dans PEP 13, tandis que le processus de décision du nouveau modèle est décrit dans PEP 8000. Pour plus d'informations, consultez le Keynote PyCon 2019 et écoutez Brett Cannon sur Talk Python To Me et sur Le podcast Changelog. Vous pouvez suivre les mises à jour du conseil de direction sur GitHub.

Autres caractéristiques assez cool

Jusqu’à présent, vous avez vu l’actualité dans les nouveautés de Python 3.8. Cependant, il y a beaucoup d'autres changements qui sont aussi très cool. Dans cette section, vous verrez rapidement certaines d’entre elles.

importlib.metadata

Un nouveau module est disponible dans la bibliothèque standard de Python 3.8: importlib.metadata. Grâce à ce module, vous pouvez accéder aux informations sur les packages installés dans votre installation Python. Avec son module d'accompagnement, importlib.resources, importlib.metadata améliore la fonctionnalité de l'ancien pkg_resources.

Par exemple, vous pouvez obtenir des informations sur pépin:

>>>

>>> de importlib importation métadonnées
>>> métadonnées.version("pépin")
'19 .2.3 '

>>> pip_metadata = métadonnées.métadonnées("pépin")
>>> liste(pip_metadata)
['Metadata-Version''Name''Version'Summary''Home-page''Author'['Metadata-Version''Name''Version'Summary''Home-page''Author'['Metadata-Version''Name''Version''Summary''Home-page''Author'['Metadata-Version''Name''Version''Summary''Home-page''Author'
    'Auteur-email', 'Licence', 'Mots-clés', 'Plateforme', 'Classificateur',
        «Classificateur», «classificateur», «classificateur», «classificateur», «classificateur»,
        «Classificateur», «classificateur», «classificateur», «classificateur», «classificateur»,
        'Classificateur', 'Classificateur', 'Nécessite Python']

>>> pip_metadata[[[["Page d'accueil"]
'https://pip.pypa.io/'

>>> pip_metadata[[[["Requiert Python"]
'> = 2,7,! = 3.0. *,! = 3.1. *,! = 3.2. *,! = 3.3. *,! = 3.4. *'

>>> len(métadonnées.des dossiers("pépin"))
668

La version actuellement installée de pépin est 19.2.3. métadonnées () donne accès à la plupart des informations que vous pouvez voir sur PyPI. Vous pouvez par exemple voir que cette version de pépin nécessite soit Python 2.7, soit Python 3.5 ou une version ultérieure. Avec des dossiers(), vous obtenez une liste de tous les fichiers qui composent le pépin paquet. Dans ce cas, il existe près de 700 fichiers.

des dossiers() retourne une liste de Chemin objets. Cela vous donne un moyen pratique d’explorer le code source d’un paquet, en utilisant read_text (). L'exemple suivant imprime __init__.py du lecteur de realpython paquet:

>>>

>>> [[[[p pour p dans métadonnées.des dossiers("realpython-reader") si p.suffixe == ".py"]
[PackagePath('lecteur/__init__py')PackagePath('lecteur/__main__py')[PackagePath('lecteur/__init__py')PackagePath('lecteur/__main__py')[PackagePath('reader/__init__py')PackagePath('reader/__main__py')[PackagePath('reader/__init__py')PackagePath('reader/__main__py')
    PackagePath ('reader / feed.py'), PackagePath ('reader / viewer.py')]

>>> init_path = _[[[[0]  # Souligner l'accès à la dernière valeur renvoyée dans le REPL
>>> impression(init_path.read_text())
"" "Lecteur de flux Real Python

Importez le module `feed` pour qu'il fonctionne avec le flux Real Python:

                >>> depuis le lecteur import feed
                >>> feed.get_titles ()
    ['Logging in Python', 'The Best Python Books', ...]

Voir https://github.com/realpython/reader/ pour plus d'informations.
"" "

# Version du paquet realpython-reader
__version__ = "1.0.0"

...

Vous pouvez également accéder aux dépendances du package:

>>>

>>> métadonnées.a besoin("realpython-reader")
['feedparser', 'html2text', 'importlib-resources', 'typing']

a besoin() liste les dépendances d'un paquet. Tu peux voir ça lecteur de realpython par exemple utilise feedparser en arrière-plan pour lire et analyser un flux d'articles.

Il y a un backport de importlib.metadata disponible sur PyPI qui fonctionne sur les versions antérieures de Python. Vous pouvez l'installer en utilisant pépin:

$ python -m pip installer importlib-metadata

Vous pouvez utiliser le backport PyPI dans votre code comme suit:

essayer:
    de importlib importation métadonnées
sauf ImportError:
    importation importlib_metadata comme métadonnées

...

Voir la documentation pour plus d'informations sur importlib.metadata

Nouveau et amélioré math et statistiques Les fonctions

Python 3.8 apporte de nombreuses améliorations aux packages et modules de bibliothèques standard existants. math dans la bibliothèque standard a quelques nouvelles fonctions. math.prod () fonctionne de manière similaire à celle intégrée somme(), mais pour les produits multiplicatifs:

>>>

>>> importation math
>>> math.prod((2, 8, sept, sept))
784

>>> 2 * 8 * sept * sept
784

Les deux déclarations sont équivalentes. prod () sera plus facile à utiliser lorsque vous avez déjà stocké les facteurs dans un itérable.

Une autre nouvelle fonction est math.isqrt (). Vous pouvez utiliser isqrt () pour trouver la partie entière des racines carrées:

>>>

>>> importation math
>>> math.isqrt(9)
3

>>> math.sqrt(9)
3.0

>>> math.isqrt(15)
3

>>> math.sqrt(15)
3.872983346207417

La racine carrée de 9 est 3. Vous pouvez voir que isqrt () renvoie un résultat entier, alors que math.sqrt () retourne toujours un flotte. La racine carrée de 15 est presque 3,9. Notez que isqrt () tronque la réponse jusqu'au nombre entier suivant, dans ce cas 3.

Enfin, vous pouvez maintenant plus facilement travailler avec n– points et vecteurs dimensionnels dans la bibliothèque standard. Vous pouvez trouver la distance entre deux points avec math.dist ()et la longueur d'un vecteur avec math.hypot ():

>>>

>>> importation math
>>> point_1 = (16, 25, 20)
>>> point_2 = (8, 15, 14)

>>> math.dist(point_1, point_2)
14.142135623730951

>>> math.hypot(*point_1)
35.79106033634656

>>> math.hypot(*point_2)
22.02271554554524

Cela facilite le travail avec des points et des vecteurs à l'aide de la bibliothèque standard. Toutefois, si vous comptez effectuer de nombreux calculs sur des points ou des vecteurs, vous devez vérifier NumPy.

le statistiques Le module a également plusieurs nouvelles fonctions:

L'exemple suivant montre les fonctions utilisées:

>>>

>>> importation statistiques
>>> Les données = [[[[9, 3, 2, 1, 1, 2, sept, 9]
>>> statistiques.moyen(Les données)
4,25

>>> statistiques.Moyenne géométrique(Les données)
3.013668912157617

>>> statistiques.multimode(Les données)
[9, 2, 1]

>>> statistiques.quantiles(Les données, n=4)
[1.25, 2.5, 8.5]

En Python 3.8, il y a une nouvelle statistics.NormalDist classe qui facilite le travail avec la distribution normale gaussienne.

Pour voir un exemple d'utilisation NormalDist, vous pouvez essayer de comparer la vitesse du nouveau statistiques.fmean () et le traditionnel statistiques.mean ():

>>>

>>> importation Aléatoire
>>> importation statistiques
>>> de temps importation temps

>>> # Créer 10 000 nombres aléatoires
>>> Les données = [[[[Aléatoire.Aléatoire() pour _ dans intervalle(10_000)]

>>> # Mesurer le temps nécessaire pour exécuter mean () et fmean ()
>>> t_moy = [[[[temps("statistics.mean (data)", nombre=100, globals=globals())
...           pour _ dans intervalle(30)]
>>> t_fmean = [[[[temps("statistics.fmean (données)", nombre=100, globals=globals())
...            pour _ dans intervalle(30)]

>>> # Créer des objets NormalDist basés sur les timings échantillonnés
>>> n_moy = statistiques.NormalDist.from_samples(t_moy)
>>> n_fmean = statistiques.NormalDist.from_samples(t_fmean)

>>> # Regardez l'échantillon moyen et l'écart type
>>> n_moy.signifier, n_moy.stdev
(0.825690647733245, 0.07788573997674526)

>>> n_fmean.signifier, n_fmean.stdev
(0.010488564966666065, 0.0008572332785645231)

>>> # Calculer le 1 centile inférieur de la moyenne
>>> n_moy.quantiles(n=100)[[[[0]
0.6445013221202459

In this example, you use timeit to measure the execution time of mean() et fmean(). To get reliable results, you let timeit execute each function 100 times, and collect 30 such time samples for each function. Based on these samples, you create two NormalDist objects. Note that if you run the code yourself, it might take up to a minute to collect the different time samples.

NormalDist has many convenient attributes and methods. See the documentation for a complete list. Inspecting .mean et .stdev, you see that the old statistics.mean() runs in 0.826 ± 0.078 seconds, while the new statistics.fmean() spends 0.0105 ± 0.0009 seconds. In other words, fmean() is about 80 times faster for these data.

If you need more advanced statistics in Python than the standard library offers, check out statsmodels et scipy.stats.

Warnings About Dangerous Syntax

Python has a SyntaxWarning which can warn about dubious syntax that is typically not a SyntaxError. Python 3.8 adds a few new ones that can help you during coding and debugging.

The difference between est et == can be confusing. The latter checks for equal values, while est est Vrai only when objects are the same. Python 3.8 will try to warn you about cases when you should use == instead of est:

>>>

>>> # Python 3.7
>>> version = "3.7"
>>> version est "3.7"
Faux

>>> # Python 3.8
>>> version = "3.8"
>>> version est "3.8"
:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
Faux

>>> version == "3.8"
Vrai

It’s easy to miss a comma when you’re writing out a long list, especially when formatting it vertically. Forgetting a comma in a list of tuples will give a confusing error message about tuples not being callable. Python 3.8 additionally emits a warning that points toward the real issue:

>>>

>>> [[[[
...   (1, 3)
...   (2, 4)
... ]
:2: SyntaxWarning: 'tuple' object is not callable; perhaps
                                            you missed a comma?
Traceback (most recent call last):
  Fichier "", line 2, in 
TypeError: 'tuple' object is not callable

The warning correctly identifies the missing comma as the real culprit.

Optimizations

There are several optimizations made for Python 3.8. Some that make code run faster. Others reduce the memory footprint. For example, looking up fields in a namedtuple is significantly faster in Python 3.8 compared with Python 3.7:

>>>

>>> importation collections
>>> de timeit importation timeit
>>> La personne = collections.namedtuple("Person", "name twitter")
>>> raymond = La personne("Raymond", "@raymondh")

>>> # Python 3.7
>>> timeit("raymond.twitter", globals=globals())
0.05876131607996285

>>> # Python 3.8
>>> timeit("raymond.twitter", globals=globals())
0.0377705999400132

You can see that looking up .twitter sur le namedtuple is 30-40% faster in Python 3.8. Lists save some space when they are initialized from iterables with a known length. This can save memory:

>>>

>>> importation sys

>>> # Python 3.7
>>> sys.getsizeof(list(intervalle(20191014)))
181719232

>>> # Python 3.8
>>> sys.getsizeof(list(intervalle(20191014)))
161528168

In this case, the list uses about 11% less memory in Python 3.8 compared with Python 3.7.

Other optimizations include better performance in subprocess, faster file copying with shutil, improved default performance in pickle, and faster operator.itemgetter opérations. See the official documentation for a complete list of optimizations.

So, Should You Upgrade to Python 3.8?

Let’s start with the simple answer. If you want to try out any of the new features you have seen here, then you do need to be able to use Python 3.8. Tools like pyenv and Anaconda make it easy to have several versions of Python installed side by side. Alternatively, you can run the official Python 3.8 Docker container. There is no downside to trying out Python 3.8 for yourself.

Now, for the more complicated questions. Should you upgrade your production environment to Python 3.8? Should you make your own project dependent on Python 3.8 to take advantage of the new features?

You should have very few issues running Python 3.7 code in Python 3.8. Upgrading your environment to run Python 3.8 is therefore quite safe, and you would be able to take advantage of the optimizations made in the new version. Different beta-versions of Python 3.8 have already been available for months, so hopefully most bugs are already squashed. However, if you want to be conservative, you might hold out until the first maintenance release (Python 3.8.1) is available.

Once you’ve upgraded your environment, you can start to experiment with features that are only in Python 3.8, such as assignment expressions and positional-only arguments. However, you should be conscious about whether other people depend on your code, as this will force them to upgrade their environment as well. Popular libraries will probably mostly support at least Python 3.6 for quite a while longer.

See Porting to Python 3.8 for more information about preparing your code for Python 3.8.