Article de reference

Syntaxe et sémantique Python

Un extrait de code Python avec des mots-clés mis en évidence en gras et en jaune La syntaxe du langage de programmation Python est l'ensemble des règles qui définissent la maniè...

Un extrait de code Python avec des mots-clés mis en évidence en gras et en jaune

La syntaxe du langage de programmation Python est l'ensemble des règles qui définissent la manière dont un programme Python sera écrit et interprété (à la fois par le système d'exécution et par les lecteurs humains). Le langage Python présente de nombreuses similitudes avec Perl , C et Java . Cependant, il existe des différences nettes entre les langages. Il prend en charge plusieurs paradigmes de programmation , notamment la programmation structurée, orientée objet et fonctionnelle , et dispose d'un système de types dynamique et d'une gestion automatique de la mémoire.

La syntaxe de Python est simple et cohérente, adhérant au principe selon lequel « il devrait y avoir une seule façon évidente de procéder, et de préférence une seule ». Le langage intègre des types et des structures de données intégrés, des mécanismes de contrôle de flux, des fonctions de première classe et des modules pour une meilleure réutilisation et organisation du code . Python utilise également des mots-clés anglais là où d'autres langages utilisent la ponctuation, contribuant ainsi à sa présentation visuelle épurée.

Le langage offre une gestion robuste des erreurs grâce aux exceptions et inclut un débogueur dans la bibliothèque standard pour une résolution efficace des problèmes. La syntaxe de Python, conçue pour la lisibilité et la facilité d'utilisation, en fait un choix populaire parmi les débutants comme les professionnels.

Philosophie de conception

Python a été conçu pour être un langage très lisible . Il a une présentation visuelle relativement épurée et utilise fréquemment des mots-clés anglais là où d'autres langages utilisent la ponctuation . Python vise à être simple et cohérent dans la conception de sa syntaxe, encapsulée dans le mantra « Il devrait y avoir une seule façon évidente de le faire », et de préférence une seule, issue du Zen de Python .

Ce mantra s'oppose délibérément au mantra de Perl et Ruby : « il y a plus d'une façon de le faire ».

Mots clés

Python possède 35 mots-clés ou mots réservés ; ils ne peuvent pas être utilisés comme identifiants .

  • and
  • as
  • assert
  • async
  • await
  • break
  • class
  • continue
  • def
  • del
  • elif
  • else
  • except
  • False
  • finally
  • for
  • from
  • global
  • if
  • import
  • in
  • is
  • lambda
  • None
  • nonlocal
  • not
  • or
  • pass
  • raise
  • return
  • True
  • try
  • while
  • with
  • yield

De plus, Python possède également 3 mots-clés souples . Contrairement aux mots-clés durs classiques , les mots-clés souples sont des mots réservés uniquement dans les contextes limités où leur interprétation en tant que mots-clés aurait un sens syntaxique. Ces mots peuvent être utilisés comme identifiants ailleurs, en d'autres termes, match et case sont des noms valides pour les fonctions et les variables.

  • _
  • case
  • match
Remarques

Échancrure

Python utilise des espaces pour délimiter les blocs de flux de contrôle (en suivant la règle du hors-jeu ). Python emprunte cette fonctionnalité à son prédécesseur ABC : au lieu de la ponctuation ou des mots-clés, il utilise l'indentation pour indiquer l'exécution d'un bloc .

Dans les langages dits « à format libre » (qui utilisent la structure de blocs dérivée d' ALGOL) , les blocs de code sont délimités par des accolades ( { }) ou des mots-clés. Dans la plupart des conventions de codage de ces langages, les programmeurs indentent généralement le code dans un bloc, pour le distinguer visuellement du code environnant.

Une fonction récursive nommée , à laquelle est passé un seul paramètre , , et si le paramètre est 0 appellera une fonction différente nommée et sinon appellera , en passant , et s'appellera également de manière récursive, en passant comme paramètre, pourrait être implémentée comme ceci en Python : fooxbarbazxx-1

def 
foo ( x ): 
si 
x 
== 
0 : 
bar () 
sinon : 
baz ( x ) 
foo ( x 
- 
1 )

et pourrait être écrit comme ceci en C avec le style d'indentation K&R :

void foo ( int x ) { si ( x == 0 ) { bar (); } sinon { baz ( x ); foo ( x - 1 ); } }

Un code mal indenté pourrait être mal interprété par un lecteur humain, de la même manière qu'il serait interprété par un compilateur ou un interpréteur. Par exemple, si l'appel de fonction foo(x - 1)sur la dernière ligne de l'exemple ci-dessus a été indenté par erreur pour être en dehors du bloc if/ :else

def 
foo ( x ): 
si 
x 
== 
0 : 
bar () 
sinon : 
baz ( x ) 
foo ( x 
- 
1 )

cela entraînerait l'exécution systématique de la dernière ligne, même lorsqu'elle xest égale à 0, ce qui entraînerait une récursivité sans fin .

Bien que les espaces et les tabulations soient acceptés comme formes d'indentation et que tout multiple d'espaces puisse être utilisé, les espaces sont recommandés et 4 espaces (comme dans les exemples ci-dessus) sont recommandés et sont de loin les plus couramment utilisés. Le mélange d'espaces et de tabulations sur des lignes consécutives n'est pas autorisé à partir de Python 3 car cela peut créer des bogues difficiles à voir, car de nombreux éditeurs de texte ne distinguent pas visuellement les espaces et les tabulations.

Structures de données

Python étant un langage typé dynamiquement , les valeurs Python, et non les variables, portent des informations de type . Toutes les variables en Python contiennent des références à des objets , et ces références sont passées aux fonctions. Certaines personnes (dont Guido van Rossum lui-même) ont appelé ce système de passage de paramètres « appel par référence d'objet ». Une référence d'objet signifie un nom, et la référence passée est un « alias », c'est-à-dire une copie de la référence au même objet, tout comme en C/ C++ . La valeur de l'objet peut être modifiée dans la fonction appelée avec l'« alias », par exemple :

>>> alist 
= 
[ 'a' , 
'b' , 
'c' ] 
>>> def 
ma_fonction ( al ): 
... 
al . append ( 'x' ) 
... 
print ( al ) 
... 
>>> ma_fonction ( alist ) 
['a', 'b', 'c', 'x'] 
>>> alist 
['a', 'b', 'c', 'x']

La fonction my_funcmodifie la valeur de alistavec l'argument formel al, qui est un alias de alist. Cependant, toute tentative d'opération (d'assignation d'une nouvelle référence d'objet à) sur l'alias lui-même n'aura aucun effet sur l'objet d'origine.

>>> alist 
= 
[ 'a' , 
'b' , 
'c' ] 
>>> def 
my_func ( al ): 
... 
# al.append('x') 
... 
al 
= 
al 
+ 
[ 'x' ] 
# une nouvelle liste créée et assignée à al signifie que al n'est plus un alias pour alist 
... 
print ( al ) 
... 
>>> my_func ( alist ) 
['a', 'b', 'c', 'x'] 
>>> print ( alist ) 
['a', 'b', 'c']

En Python, les noms accessibles non locaux et non déclarés globaux sont tous des alias.

Parmi les langages à typage dynamique, Python est modérément typé. La conversion implicite est définie pour les types numériques (ainsi que pour les booléens ), de sorte qu'on peut multiplier validement un nombre complexe par un entier (par exemple) sans conversion explicite . Cependant, il n'y a pas de conversion implicite entre, par exemple, les nombres et les chaînes ; une chaîne est un argument invalide pour une fonction mathématique qui attend un nombre.

Types de bases

Python propose une large gamme de types de données de base. Outre l'arithmétique conventionnelle des nombres entiers et des nombres à virgule flottante , il prend en charge de manière transparente l'arithmétique de précision arbitraire , les nombres complexes et les nombres décimaux .

Python prend en charge une grande variété d'opérations sur les chaînes. Les chaînes en Python sont immuables , donc une opération sur une chaîne telle qu'une substitution de caractères , qui dans d'autres langages de programmation peut modifier la chaîne en place , renvoie une nouvelle chaîne en Python. Des considérations de performances poussent parfois à utiliser des techniques spéciales dans les programmes qui modifient les chaînes de manière intensive, comme la jonction de tableaux de caractères en chaînes uniquement si nécessaire.

Types de collections

L'un des aspects les plus utiles de Python est le concept de types de collection (ou conteneur ). En général, une collection est un objet qui contient d'autres objets d'une manière qui est facilement référencée ou indexée . Les collections se présentent sous deux formes de base : les séquences et les mappages .

Les types séquentiels ordonnés sont les listes ( tableaux dynamiques ), les tuples et les chaînes. Toutes les séquences sont indexées positionnellement (de 0 à length - 1 ) et toutes, à l'exception des chaînes, peuvent contenir n'importe quel type d'objet, y compris plusieurs types dans la même séquence. Les chaînes et les tuples sont immuables, ce qui en fait des candidats parfaits pour les clés de dictionnaire (voir ci-dessous). Les listes, en revanche, sont mutables ; les éléments peuvent être insérés, supprimés, modifiés, ajoutés ou triés sur place .

Les mappages, en revanche, sont des types (souvent non ordonnés) implémentés sous la forme de dictionnaires qui « mappent » un ensemble de clés immuables à des éléments correspondants (un peu comme une fonction mathématique). Par exemple, on pourrait définir un dictionnaire ayant une chaîne "toast"mappée à l'entier 42ou vice versa. Les clés d'un dictionnaire doivent être d'un type Python immuable, tel qu'un entier ou une chaîne, car en réalité, elles sont implémentées via une fonction de hachage . Cela permet des temps de recherche beaucoup plus rapides, mais nécessite de ne pas modifier les clés.

Les dictionnaires sont au cœur de Python car ils se trouvent au cœur de tous les objets et de toutes les classes : les correspondances entre les noms de variables (chaînes) et les valeurs auxquelles les noms font référence sont stockées sous forme de dictionnaires (voir Système d'objets). Étant donné que ces dictionnaires sont directement accessibles (via un __dict__attribut d'objet), la métaprogrammation est un processus simple et naturel en Python.

Un type de collection d'ensembles est une collection non indexée et non ordonnée qui ne contient aucun doublon et implémente des opérations théoriques sur les ensembles telles que l'union , l'intersection , la différence , la différence symétrique et le test de sous-ensemble . Il existe deux types d'ensembles : setet frozenset, la seule différence étant qu'il setest mutable et frozensetqu'il est immuable. Les éléments d'un ensemble doivent être hachables. Ainsi, par exemple, a frozensetpeut être un élément d'un ensemble régulier setalors que l'inverse n'est pas vrai.

Python fournit également des capacités étendues de manipulation de collection telles que la vérification de confinement intégrée et un protocole d'itération générique.

Système d'objets

En Python, tout est un objet, même les classes. Les classes, en tant qu'objets, ont une classe, appelée métaclasse . Python prend également en charge l'héritage multiple et les mixins .

Le langage prend en charge une introspection étendue des types et des classes. Les types peuvent être lus et comparés (les types sont des instances de ) type. Les attributs d'un objet peuvent être extraits sous forme de dictionnaire.

Les opérateurs peuvent être surchargés en Python en définissant des fonctions membres spéciales. Par exemple, la définition d'une méthode nommée __add__sur une classe permet d'utiliser l' +opérateur sur les objets de cette classe.

Littéraux

Cordes

Python possède différents types de chaînes littérales .

Littéraux de chaîne normaux

Les guillemets simples ou doubles peuvent être utilisés pour citer des chaînes. Contrairement aux langages shell Unix, à Perl ou aux langages influencés par Perl tels que Ruby ou Groovy , les guillemets simples et doubles fonctionnent de manière identique, c'est-à-dire qu'il n'y a pas d'interpolation de chaîne des expressions $foo . Cependant, l'interpolation peut être effectuée de différentes manières : avec des « f-strings » (depuis Python 3.6 ), en utilisant la formatméthode ou l'ancien opérateur de format de chaîne % .

Par exemple, toutes ces instructions Python :

print ( f "Je viens d'imprimer { num } pages sur l'imprimante { printer } " )
print ( "Je viens d'imprimer {} pages sur l'imprimante {} " . format ( num , 
printer )) 
print ( "Je viens d'imprimer {0} pages sur l'imprimante {1} " . format ( num , 
printer )) 
print ( "Je viens d'imprimer {num} pages sur l'imprimante {printer} " . format ( num = num , 
printer = printer ))
print ( "Je viens d'imprimer %s pages sur l'imprimante %s " 
% 
( num , 
printer )) 
print ( "Je viens d'imprimer %(num)s pages sur l'imprimante %(printer)s " 
% 
{ "num" : 
num , 
"printer" : 
printer })

sont équivalents à l'instruction Perl :

imprimer "Je viens d'imprimer $num pages sur l'imprimante $printer "

Ils construisent une chaîne en utilisant les variables numet printer.

Littéraux de chaîne multiligne

Il existe également des chaînes multilignes, qui commencent et se terminent par une série de trois guillemets simples ou doubles et fonctionnent comme ici dans les documents en Perl et Ruby .

Un exemple simple avec interpolation de variable (en utilisant la formatméthode) est :

imprimer ( '''Cher {destinataire} ,
Je te souhaite de quitter Sunnydale et de ne jamais revenir.
Pas tout à fait amour, 
{sender} 
''' . format ( sender = "Buffy contre les vampires" , 
recipient = "Spike" ))

Cordes brutes

Enfin, tous les types de chaînes mentionnés précédemment sont disponibles en variétés « brutes » (indiquées en plaçant un r littéral avant la citation d'ouverture), qui ne font pas d'interpolation par barre oblique inverse et sont donc très utiles pour les expressions régulières ; comparer « @-quoting » en C# . Les chaînes brutes ont été initialement incluses spécifiquement pour les expressions régulières. En raison des limitations du tokenizer, les chaînes brutes peuvent ne pas avoir de barre oblique inverse de fin. La création d'une chaîne brute contenant un chemin Windows se terminant par une barre oblique inverse nécessite une certaine variété de solutions de contournement (généralement, l'utilisation de barres obliques au lieu de barres obliques inverses, car Windows accepte les deux).

Voici quelques exemples :

>>> # Un chemin Windows, même les chaînes brutes ne peuvent pas se terminer par une barre oblique inverse 
>>> r "C:\Foo\Bar\Baz \" Fichier "<stdin>" , ligne 1 
r "C:\Foo\Bar\Baz \" ^ SyntaxError : EOL lors de l'analyse du littéral de chaîne
>>> dos_path 
= 
r "C:\Foo\Bar\Baz\ " 
# évite l'erreur en ajoutant 
>>> dos_path . rstrip () 
# et en supprimant l'espace de fin 
'C:\\Foo\\Bar\\Baz\\'
>>> quoted_dos_path 
= 
r '" {} "' . format ( dos_path ) 
>>> quoted_dos_path 
'"C:\\Foo\\Bar\\Baz\\ "'
>>> # Une expression régulière correspondant à une chaîne entre guillemets avec une éventuelle barre oblique inverse 
>>> re . match ( r '"(([^" \\ ]| \\ .)*)"' , 
quoted_dos_path ) . group ( 1 ) . rstrip () 
'C:\\Foo\\Bar\\Baz\\'
>>> code 
= 
'foo(2, bar)' 
>>> # Inverser les arguments dans un appel de fonction à deux arguments 
>>> re . sub ( r '\(([^,]*?),([^ ,]*?)\)' , 
r '(\2, \1)' , 
code ) 
'foo(2, bar)' 
>>> # Notez que cela ne fonctionnera pas si l'un des arguments contient des parenthèses ou des virgules.

Concaténation de chaînes littérales adjacentes

Les littéraux de chaîne (utilisant éventuellement des conventions de citation différentes) apparaissant de manière contiguë et uniquement séparés par des espaces (y compris les nouvelles lignes), sont autorisés et sont agrégés en une seule chaîne plus longue. Ainsi

titre 
= 
"Un bon geste : " \ 'Une histoire naturelle du tournevis et de la vis'

est équivalent à

titre 
= 
« Un bon geste : une histoire naturelle du tournevis et de la vis »

Unicode

Depuis Python 3.0, le jeu de caractères par défaut est UTF-8, à la fois pour le code source et pour l'interpréteur. En UTF-8, les chaînes Unicode sont traitées comme des chaînes d'octets traditionnelles. Cet exemple fonctionnera :

s 
= 
"Γειά" 
# Bonjour en grec 
print ( s )

Nombres

Les littéraux numériques en Python sont du type normal, par exemple 0, -1, 3.4, 3.5e-8.

Python possède des entiers de longueur arbitraire et augmente automatiquement leur taille de stockage si nécessaire. Avant Python 3, il existait deux types de nombres entiers : les entiers traditionnels de taille fixe et les entiers « longs » de taille arbitraire. La conversion en entiers « longs » était effectuée automatiquement lorsque cela était nécessaire, et le programmeur n'avait donc généralement pas besoin de connaître les deux types d'entiers. Dans les versions de langage plus récentes, la distinction a complètement disparu et tous les entiers se comportent comme des entiers de longueur arbitraire.

Python prend en charge les nombres à virgule flottante normaux , qui sont créés lorsqu'un point est utilisé dans un littéral (par exemple 1.1), lorsqu'un entier et un nombre à virgule flottante sont utilisés dans une expression, ou à la suite de certaines opérations mathématiques (« vraie division » via l' /opérateur, ou exponentiation avec un exposant négatif).

Python prend également en charge les nombres complexes de manière native. Les nombres complexes sont indiqués par le suffixe Jou , par exemple . j3 + 4j

Listes, tuples, ensembles, dictionnaires

Python dispose d'un support syntaxique pour la création de types de conteneurs.

Les listes (classe list) sont des séquences mutables d'éléments de types arbitraires et peuvent être créées soit avec la syntaxe spéciale

a_list 
= 
[ 1 , 
2 , 
3 , 
"un chien" ]

ou en utilisant la création d'objet normale

a_second_list 
= 
[] 
a_second_list . append ( 4 ) 
a_second_list . append ( 5 )

Les tuples (classe tuple) sont des séquences immuables d'éléments de types arbitraires. Il existe également une syntaxe spéciale pour créer des tuples

a_tuple 
= 
1 , 
2 , 
3 , 
"quatre" 
a_tuple 
= 
( 1 , 
2 , 
3 , 
"quatre" )

Bien que les tuples soient créés en séparant les éléments par des virgules, l'ensemble de la construction est généralement entouré de parenthèses pour améliorer la lisibilité. Un tuple vide est indiqué par (), tandis qu'un tuple avec une seule valeur peut être créé avec (1,).

Les ensembles (classe set) sont des conteneurs mutables d'éléments hachables de types arbitraires, sans doublons. Les éléments ne sont pas ordonnés, mais les ensembles prennent en charge l'itération sur les éléments. La syntaxe de création d'ensemble utilise des accolades

some_set 
= 
{ 0 , 
(), 
False }

Les ensembles Python ressemblent beaucoup aux ensembles mathématiques et prennent en charge des opérations telles que l'intersection et l'union d'ensembles . Python propose également une frozensetclasse pour les ensembles immuables, voir Types de collection.

Les dictionnaires (classe dict) sont des mappages mutables reliant les clés et les valeurs correspondantes. Python a une syntaxe spéciale pour créer des dictionnaires ( {key: value})

a_dictionary 
= 
{ "clé 1" : 
"valeur 1" , 
2 : 
3 , 
4 : 
[]}

La syntaxe du dictionnaire est similaire à la syntaxe de l'ensemble, la différence est la présence de deux points. Le littéral vide {}produit un dictionnaire vide plutôt qu'un ensemble vide, qui est plutôt créé à l'aide du constructeur non littéral : set().

Opérateurs

Arithmétique

Python inclut les opérateurs +, -, *, /(« division vraie »), //( division au sol ), %( module ) et **( exponentiation ), avec leur priorité mathématique habituelle .

En Python 3, x / yeffectue une « vraie division », ce qui signifie qu'il renvoie toujours un flottant, même si xet ysont des entiers qui se divisent uniformément.

>>> 4 
/ 
2 
2.0

et //effectue une division entière ou une division par le plancher , renvoyant le plancher du quotient sous forme d'entier.

Dans Python 2 (et la plupart des autres langages de programmation), sauf demande explicite, une division entièrex / y était effectuée , renvoyant un nombre à virgule flottante uniquement si l'une des entrées était un nombre à virgule flottante. Cependant, comme Python est un langage typé dynamiquement, il n'était pas toujours possible de savoir quelle opération était effectuée, ce qui conduisait souvent à des bugs subtils, ce qui a conduit à l'introduction de l' opérateur et au changement de sémantique de l' opérateur dans Python 3. ///

Opérateurs de comparaison

Les opérateurs de comparaison, c'est-à -dire ==, !=, <, >, <=, >=, is, is not, inet not in sont utilisés sur toutes sortes de valeurs. Les nombres, les chaînes, les séquences et les mappages peuvent tous être comparés. Dans Python 3, les types disparates (tels que a stret an int) n'ont pas d'ordre relatif cohérent, et les tentatives de comparaison de ces types génèrent une TypeErrorexception. Bien qu'il soit possible de comparer des types disparates dans Python 2 (par exemple, si une chaîne était supérieure ou inférieure à un entier), l'ordre n'était pas défini ; cela était considéré comme une bizarrerie de conception historique et a finalement été supprimé dans Python 3.

Les expressions de comparaison enchaînées telles que a < b < cont à peu près la même signification qu'en mathématiques, plutôt que la signification inhabituelle que l'on trouve en C et dans des langages similaires. Les termes sont évalués et comparés dans l'ordre. L'opération a une sémantique de court-circuit , ce qui signifie que l'évaluation est garantie de s'arrêter dès qu'un verdict est clair : si a < best faux, cn'est jamais évalué car l'expression ne peut plus être vraie.

Pour les expressions sans effets secondaires, a < b < cest équivalent à a < b and b < c. Cependant, il existe une différence substantielle lorsque les expressions ont des effets secondaires. a < f(x) < bl'évaluera f(x)exactement une fois, alors que a < f(x) and f(x) < bl'évaluera deux fois si la valeur de aest inférieure à f(x)et une fois sinon.

Opérateurs logiques

Dans toutes les versions de Python, les opérateurs booléens traitent les valeurs nulles ou vides telles que , 0, None, 0.0, [], et {}comme fausses, alors qu'en général, ils traitent les valeurs non vides et non nulles comme vraies. Les valeurs booléennes Trueet Falseont été ajoutées au langage dans Python 2.2.1 en tant que constantes (sous-classées de 1et 0) et ont été modifiées pour devenir des mots-clés à part entière dans Python 3. Les opérateurs de comparaison binaire tels que ==et >renvoient soit Trueou False.

Les opérateurs booléens andet orutilisent une évaluation minimale . Par exemple, y == 0 or x/y > 100ne génèreront jamais d'exception de division par zéro. Ces opérateurs renvoient la valeur du dernier opérande évalué, plutôt que Trueou False. Ainsi, l'expression (4 and 5)est évaluée à 5, et (4 or 5)est évaluée à 4.

Programmation fonctionnelle

Comme mentionné ci-dessus, un autre point fort de Python est la disponibilité d'un style de programmation fonctionnel . Comme on peut s'y attendre, cela rend le travail avec des listes et d'autres collections beaucoup plus simple.

Compréhensions

Une telle construction est la compréhension de liste , qui peut être exprimée avec le format suivant :

L 
= 
[ expression_de_mapping 
pour 
l'élément 
dans 
la liste_source 
si 
expression_de_filtre ]

Utiliser la compréhension de liste pour calculer les cinq premières puissances de deux :

powers_of_two 
= 
[ 2 ** n 
pour 
n 
dans 
la plage ( 1 , 
6 )]

L' algorithme Quicksort peut être exprimé de manière élégante (bien qu'inefficace) à l'aide de compréhensions de listes :

def 
qsort ( L ): 
si 
L 
== 
[]: 
retour 
[] 
pivot 
= 
L [ 0 ] 
retour 
( qsort ([ x 
pour 
x 
dans 
L [ 1 :] 
si 
x 
< 
pivot ]) 
+ 
[ pivot ] 
+ 
qsort ([ x 
pour 
x 
dans 
L [ 1 :] 
si 
x 
>= 
pivot ]))

Python 2.7+ prend également en charge les compréhensions d'ensembles et les compréhensions de dictionnaires.

Des fonctions de premier ordre

En Python, les fonctions sont des objets de première classe qui peuvent être créés et transmis de manière dynamique.

La prise en charge limitée des fonctions anonymes par Python est la lambdaconstruction. Un exemple est la fonction anonyme qui met au carré son entrée, appelée avec l'argument 5 :

f 
= 
lambda 
x : 
x ** 2 
f ( 5 )

Les lambdas sont limités à contenir une expression plutôt que des instructions , bien que le flux de contrôle puisse toujours être implémenté de manière moins élégante dans lambda en utilisant un court-circuit, et de manière plus idiomatique avec des expressions conditionnelles.

Fermetures

Python prend en charge les fermetures lexicales depuis la version 2.2. Voici un exemple de fonction qui renvoie une fonction qui se rapproche de la dérivée de la fonction donnée :

def 
derived ( f , 
dx ): 
Renvoie une fonction qui approxime la dérivée de f  en utilisant un intervalle de dx, qui doit être suffisamment petit. def function ( x ): return ( f ( x + dx ) -f ( x ) ) / dx return function

Cependant, la syntaxe de Python conduit parfois les programmeurs d'autres langages à penser que les fermetures ne sont pas prises en charge. La portée d'une variable en Python est implicitement déterminée par la portée dans laquelle on attribue une valeur à la variable, à moins que la portée ne soit explicitement déclarée avec globalou nonlocal.

Notez que la liaison d'un nom à une valeur par la fermeture n'est pas modifiable depuis la fonction. Étant donné :

>>> def 
foo ( a , 
b ): 
... 
print ( f 'a: { a } ' ) 
... 
print ( f 'b: { b } ' ) 
... 
def 
bar ( c ): 
... 
b 
= 
c 
... 
print ( f 'b*: { b } ' ) 
... 
bar ( a ) 
... 
print ( f 'b: { b } ' ) 
... 
>>> foo ( 1 , 
2 ) 
a: 1 
b: 2 
b*: 1 
b: 2

et vous pouvez voir que b, comme visible depuis la portée de la fermeture, conserve la valeur qu'elle avait ; la liaison modifiée de bl'intérieur de la fonction interne ne s'est pas propagée. Le moyen de contourner ce problème est d'utiliser une nonlocal binstruction dans bar. Dans Python 2 (qui manque nonlocalde ), la solution de contournement habituelle consiste à utiliser une valeur mutable et à modifier cette valeur, pas la liaison. Par exemple, une liste avec un élément.

Générateurs

Introduits dans Python 2.2 en tant que fonctionnalité facultative et finalisés dans la version 2.3, les générateurs sont le mécanisme de Python pour l'évaluation paresseuse d'une fonction qui renverrait autrement une liste prohibitive en termes d'espace ou de calcul.

Voici un exemple pour générer paresseusement les nombres premiers :

à partir d' 
itertools 
importer 
le nombre
def 
generate_primes ( stop_at = None ): 
primes 
= 
[] 
for 
n 
in 
count ( start = 2 ): 
if 
stop_at 
is 
not 
None 
and 
n 
> 
stop_at : 
return 
# lève l'exception StopIteration 
composite 
= 
False 
for 
p 
in 
primes : 
if 
not 
n 
% 
p : 
composite 
= 
True 
break 
elif 
p 
** 
2 
> 
n : 
break 
if 
not 
composite : 
primes . append ( n ) 
yield 
n

Lors de l'appel de cette fonction, la valeur renvoyée peut être parcourue comme une liste :

pour 
i 
dans 
generate_primes ( 100 ): 
# itérer sur les nombres premiers entre 0 et 100 
print ( i )
for 
i 
in 
generate_primes (): 
# itérer sur TOUS les nombres premiers indéfiniment 
print ( i )

La définition d'un générateur semble identique à celle d'une fonction, sauf que le mot clé yieldest utilisé à la place de return. Cependant, un générateur est un objet avec un état persistant, qui peut entrer et sortir à plusieurs reprises de la même portée. Un appel de générateur peut alors être utilisé à la place d'une liste ou d'une autre structure dont les éléments seront parcourus par itération. Chaque fois que la forboucle de l'exemple requiert l'élément suivant, le générateur est appelé et génère l'élément suivant.

Les générateurs ne doivent pas nécessairement être infinis comme dans l'exemple de nombres premiers ci-dessus. Lorsqu'un générateur se termine, une exception interne est levée, ce qui indique à tout contexte d'appel qu'il n'y a plus de valeurs. Une forboucle ou une autre itération se terminera alors.

Expressions génératrices

Introduites dans Python 2.4, les expressions génératrices sont l' équivalent de l'évaluation paresseuse des compréhensions de listes. En utilisant le générateur de nombres premiers fourni dans la section ci-dessus, nous pourrions définir une collection paresseuse, mais pas tout à fait infinie.

depuis 
itertools 
importer 
islice
primes_under_million 
= 
( i 
pour 
i 
dans 
generate_primes () 
si 
i 
< 
1000000 ) 
deux_millième_nombre_prime 
= 
islice ( primes_under_million , 
1999 , 
2000 ) . next ()

La majeure partie de la mémoire et du temps nécessaires pour générer autant de nombres premiers ne sera pas utilisée tant que l'élément requis n'aura pas été réellement accédé. Malheureusement, vous ne pouvez pas effectuer une indexation et un découpage simples des générateurs, mais vous devez utiliser le module itertools ou créer vos propres boucles. En revanche, une compréhension de liste est fonctionnellement équivalente, mais elle est gourmande en exécution de tout le travail :

primes_under_million 
= 
[ i 
pour 
i 
dans 
generate_primes ( 2000000 ) 
si 
i 
< 
1000000 ] 
deux_millième_prime 
= 
primes_under_million [ 1999 ]

La compréhension de liste créera immédiatement une grande liste (avec 78498 éléments, dans l'exemple, mais créant de manière transitoire une liste de nombres premiers de moins de deux millions), même si la plupart des éléments ne sont jamais consultés. La compréhension par générateur est plus parcimonieuse.

Dictionnaire et compréhensions d'ensembles

Alors que les listes et les générateurs avaient des compréhensions/expressions, dans les versions Python antérieures à 2.7, les autres types de collections intégrés à Python (dicts et ensembles) devaient être intégrés à l'aide de listes ou de générateurs :

>>> 
dict (( n , 
n * n ) 
pour 
n 
dans 
la plage ( 5 )) 
{ 0 : 
0 , 
1 : 
1 , 
2 : 
4 , 
3 : 
9 , 
4 : 
16 }

Python 2.7 et 3.0 ont unifié tous les types de collections en introduisant des compréhensions de dictionnaire et d'ensemble, similaires aux compréhensions de liste :

>>> 
[ n * n 
pour 
n 
dans 
la plage ( 5 )] 
# compréhension de liste régulière 
[ 0 , 
1 , 
4 , 
9 , 
16 ] 
>>> 
>>> 
{ n * n 
pour 
n 
dans 
la plage ( 5 )} 
# compréhension d'ensemble 
{ 0 , 
1 , 
4 , 
9 , 
16 } 
>>> 
>>> 
{ n : 
n * n 
pour 
n 
dans 
la plage ( 5 )} 
# compréhension de dict 
{ 0 : 
0 , 
1 : 
1 , 
2 : 
4 , 
3 : 
9 , 
4 : 
16 }

Objets

Python prend en charge la plupart des techniques de programmation orientée objet (OOP). Il autorise le polymorphisme , non seulement au sein d'une hiérarchie de classes , mais aussi par typage de canard . Tout objet peut être utilisé pour n'importe quel type, et cela fonctionnera tant qu'il possède les méthodes et les attributs appropriés. Et tout dans Python est un objet, y compris les classes, les fonctions, les nombres et les modules. Python prend également en charge les métaclasses , un outil avancé pour améliorer les fonctionnalités des classes. Naturellement, l'héritage , y compris l'héritage multiple , est pris en charge. Python prend en charge de manière très limitée les variables privées utilisant la manipulation de noms , qui est rarement utilisée dans la pratique, car la dissimulation d'informations est considérée par certains comme non pythonique , dans la mesure où elle suggère que la classe en question contient des éléments internes inesthétiques ou mal planifiés. Le slogan « nous sommes tous des utilisateurs responsables ici » est utilisé pour décrire cette attitude.

Comme c'est le cas pour les modules, les classes en Python ne mettent pas de barrière absolue entre la définition et l'utilisateur, mais comptent plutôt sur la politesse de l'utilisateur pour ne pas « pénétrer dans la définition ».

—  9. Classes, Le didacticiel Python 2.6 (2013)

Les doctrines de la programmation orientée objet (OOP) telles que l'utilisation de méthodes d'accès pour lire les membres de données ne sont pas appliquées en Python. Tout comme Python propose des constructions de programmation fonctionnelle mais ne tente pas d'exiger une transparence référentielle , il propose un système d'objets mais n'exige pas de comportement OOP . De plus, il est toujours possible de redéfinir la classe à l'aide de propriétés (voir Propriétés) de sorte que lorsqu'une certaine variable est définie ou récupérée dans le code appelant, elle invoque réellement un appel de fonction, ce qui spam.eggs = toastpourrait réellement invoquer spam.set_eggs(toast). Cela annule l'avantage pratique des fonctions d'accès, et cela reste de la programmation orientée objet car la propriété eggsdevient une partie légitime de l'interface de l'objet : elle n'a pas besoin de refléter un détail d'implémentation.

Dans la version 2.2 de Python, des classes « nouveau style » ont été introduites. Avec les classes nouveau style, les objets et les types ont été unifiés, ce qui permet de sous-classer les types. Il est même possible de définir des types entièrement nouveaux, avec un comportement personnalisé pour les opérateurs infixes. Cela permet de réaliser de nombreuses choses radicales syntaxiquement dans Python. Un nouvel ordre de résolution des méthodes pour l'héritage multiple a également été adopté avec Python 2.3. Il est également possible d'exécuter du code personnalisé lors de l'accès ou de la définition des attributs, bien que les détails de ces techniques aient évolué entre les versions de Python.

Avec déclaration

L' withinstruction gère les ressources et permet aux utilisateurs de travailler avec le protocole Context Manager. Une fonction ( __enter__()) est appelée lors de l'entrée dans la portée et une autre ( __exit__()) lors de la sortie. Cela évite d'oublier de libérer la ressource et gère également des situations plus complexes telles que la libération de la ressource lorsqu'une exception se produit pendant son utilisation. Les gestionnaires de contexte sont souvent utilisés avec des fichiers, des connexions à des bases de données, des cas de test, etc.

Propriétés

Les propriétés permettent d'invoquer des méthodes spécialement définies sur une instance d'objet en utilisant la même syntaxe que celle utilisée pour l'accès aux attributs. Voici un exemple de classe définissant certaines propriétés :

classe 
MyClass : 
def 
__init__ ( self ): 
self . _a 
= 
None
@property 
def 
a ( self ): 
renvoie 
self . _a
@a . setter 
# rend la propriété accessible en écriture 
def 
a ( self , 
value ): 
self . _a 
= 
value

Descripteurs

Une classe qui définit une ou plusieurs des trois méthodes spéciales __get__(self, instance, owner), __set__(self, instance, value), __delete__(self, instance)peut être utilisée comme descripteur. La création d'une instance d'un descripteur en tant que membre de classe d'une seconde classe fait de l'instance une propriété de la seconde classe.

Classe et méthodes statiques

Python permet la création de méthodes de classe et de méthodes statiques via l'utilisation des décorateurs @classmethod et @staticmethod. Le premier argument d'une méthode de classe est l'objet de classe au lieu de l'auto-référence à l'instance. Une méthode statique n'a pas de premier argument spécial. Ni l'instance, ni l'objet de classe ne sont passés à une méthode statique.

Exceptions

Python prend en charge (et utilise largement) la gestion des exceptions comme moyen de tester les conditions d'erreur et d'autres événements « exceptionnels » dans un programme.

Le style Python exige l'utilisation d'exceptions chaque fois qu'une condition d'erreur peut survenir. Plutôt que de tester l'accès à un fichier ou à une ressource avant de l'utiliser réellement, il est conventionnel en Python de simplement essayer de l'utiliser, en interceptant l'exception si l'accès est rejeté.

Les exceptions peuvent également être utilisées comme un moyen plus général de transfert de contrôle non local, même lorsqu'il n'y a pas d'erreur en cause. Par exemple, le logiciel de liste de diffusion Mailman , écrit en Python, utilise des exceptions pour sortir d'une logique de gestion des messages profondément imbriquée lorsqu'une décision a été prise de rejeter un message ou de le conserver pour approbation par un modérateur.

Les exceptions sont souvent utilisées comme alternative au ifbloc -block, en particulier dans les situations de threads . Une devise couramment invoquée est EAFP, ou « Il est plus facile de demander pardon que la permission », qui est attribuée à Grace Hopper . L'alternative, connue sous le nom de LBYL, ou « Regardez avant de sauter », teste explicitement les conditions préalables.

Dans ce premier exemple de code, suivant l'approche LBYL, il y a une vérification explicite de l'attribut avant l'accès :

si 
hasattr ( spam , 
'oeufs' ) : ham 
= 
spam.oeufs 
sinon : handle_missing_attr ( )

Ce deuxième échantillon suit le paradigme EAFP :

essayez : 
ham 
= 
spam . eggs 
sauf 
AttributeError : 
handle_missing_attr ()

Ces deux exemples de code ont le même effet, bien qu'il y ait des différences de performances. Lorsque spampossède l'attribut eggs, l'exemple EAFP s'exécutera plus rapidement. Lorsque spamn'a pas l'attribut eggs(le cas « exceptionnel »), l'exemple EAFP s'exécutera plus lentement. Le profileur Python peut être utilisé dans des cas spécifiques pour déterminer les caractéristiques de performances. Si les cas exceptionnels sont rares, la version EAFP aura des performances moyennes supérieures à l'alternative. De plus, il évite toute la classe des vulnérabilités de type time-of-check-to-time-of-use (TOCTTOU), d'autres conditions de concurrence , et est compatible avec le typage canard . Un inconvénient d'EAFP est qu'il ne peut être utilisé qu'avec des instructions ; une exception ne peut pas être interceptée dans une expression de générateur, une compréhension de liste ou une fonction lambda.

Commentaires et docstrings

Python propose deux méthodes pour annoter le code Python. L'une consiste à utiliser des commentaires pour indiquer ce que fait une partie du code. Les commentaires sur une seule ligne commencent par le caractère dièse ( #) et continuent jusqu'à la fin de la ligne. Les commentaires sur plusieurs lignes sont obtenus en insérant une chaîne multiligne (avec ou '''comme délimiteur à chaque extrémité) qui n'est pas utilisée dans l'affectation ou évaluée d'une autre manière, mais qui se trouve entre d'autres instructions.

Commenter un morceau de code :

importer 
le système
def 
getline ( ) : 
return 
sys.stdin.readline () # Récupère une ligne et la renvoie

Commenter un morceau de code avec plusieurs lignes :

def 
getline (): 
Cette fonction obtient une ligne et la renvoie.
 À titre de démonstration, il s’agit d’une docstring multiligne.
 Cette chaîne complète est accessible sous la forme getline.__doc__. 
return 
sys . stdin . readline ()

Les docstrings (chaînes de documentation), c'est-à-dire les chaînes qui se trouvent seules sans affectation comme première ligne indentée dans un module, une classe, une méthode ou une fonction, définissent automatiquement leur contenu comme un attribut nommé __doc__, qui est destiné à stocker une description lisible par l'homme de l'objectif, du comportement et de l'utilisation de l'objet. La fonction intégrée helpgénère sa sortie en fonction __doc__des attributs. Ces chaînes peuvent être délimitées par "ou 'pour les chaînes à une seule ligne, ou peuvent s'étendre sur plusieurs lignes si elles sont délimitées par ou '''qui est la notation de Python pour spécifier des chaînes multilignes. Cependant, le guide de style du langage spécifie que les guillemets doubles triples ( ) sont préférés pour les docstrings à une seule ligne et à plusieurs lignes.

Docstring sur une seule ligne :

def 
getline (): 
Obtenir une ligne depuis stdin et la renvoyer.""" return sys . stdin . readline ()

Docstring multiligne :

def 
getline (): 
Obtient une ligne  de stdin  et la renvoie. return sys . stdin . readline ()

Les docstrings peuvent être aussi grands que le programmeur le souhaite et contenir des sauts de ligne . Contrairement aux commentaires, les docstrings sont eux-mêmes des objets Python et font partie du code interprété que Python exécute. Cela signifie qu'un programme en cours d'exécution peut récupérer ses propres docstrings et manipuler ces informations, mais l'utilisation normale consiste à donner aux autres programmeurs des informations sur la manière d'invoquer l'objet documenté dans la docstring.

Il existe des outils permettant d'extraire les docstrings du code Python et de générer la documentation. La documentation des docstrings est également accessible depuis l'interpréteur avec la help()fonction, ou depuis le shell avec la commande pydocpydoc .

Le module standard doctest utilise les interactions copiées à partir des sessions shell Python dans des docstrings pour créer des tests, tandis que le module docopt les utilise pour définir des options de ligne de commande.

Annotations de fonctions

Les annotations de fonction (type hints) sont définies dans la PEP 3107. Elles permettent d'attacher des données aux arguments et au retour d'une fonction. Le comportement des annotations n'est pas défini par le langage et est laissé à des frameworks tiers. Par exemple, une bibliothèque pourrait être écrite pour gérer le typage statique :

def 
haul ( élément : 
Haulable , 
* vargs : 
PackAnimal ) 
-> 
Distance

Décorateurs

Un décorateur est un objet Python appelable qui est utilisé pour modifier une définition de fonction, de méthode ou de classe. Un décorateur reçoit l'objet d'origine en cours de définition et renvoie un objet modifié, qui est ensuite lié au nom dans la définition. Les décorateurs Python ont été inspirés en partie par les annotations Java et ont une syntaxe similaire ; la syntaxe du décorateur est un pur sucre syntaxique , utilisant @comme mot-clé :

@viking_chorus 
def 
menu_item (): 
print ( "spam" )

est équivalent à

def 
menu_item (): 
print ( "spam" ) 
menu_item 
= 
viking_chorus ( menu_item )

Les décorateurs sont une forme de métaprogrammation ; ils améliorent l'action de la fonction ou de la méthode qu'ils décorent. Par exemple, dans l'exemple ci-dessous, viking_choruspeut menu_itemêtre exécuté 8 fois (voir le croquis Spam ) à chaque fois qu'il est appelé :

def 
viking_chorus ( mafunc ): 
def 
inner_func ( * args , 
** kwargs ): 
pour 
i 
dans 
la plage ( 8 ): 
mafunc ( * args , 
** kwargs ) 
return 
inner_func

Les utilisations canoniques des décorateurs de fonctions sont la création de méthodes de classe ou de méthodes statiques , l'ajout d'attributs de fonction, le traçage , la définition de pré- et post-conditions et la synchronisation , mais peuvent être utilisées pour bien plus, y compris l'élimination de la récursivité terminale , la mémorisation et même l'amélioration de l'écriture d'autres décorateurs.

Les décorateurs peuvent être enchaînés en plaçant plusieurs sur des lignes adjacentes :

@invincible 
@favourite_colour ( "Bleu" ) 
def 
black_knight (): 
pass

est équivalent à

def 
black_knight (): 
pass 
black_knight 
= 
invincible ( favorite_colour ( "Bleu" )( black_knight ))

ou, en utilisant des variables intermédiaires

def 
black_knight (): 
pass 
blue_decorator 
= 
favorite_colour ( "Bleu" ) 
décoré_par_bleu 
= 
blue_decorator ( black_knight ) 
black_knight 
= 
invincible ( décoré_par_bleu )

Dans l'exemple ci-dessus, la fabriquefavourite_colour de décorateurs prend un argument. Les fabriques de décorateurs doivent renvoyer un décorateur, qui est ensuite appelé avec l'objet à décorer comme argument :

def 
favorite_colour ( couleur ) : 
def 
decorator ( func ) : 
def 
wrapper (): 
print ( couleur ) 
func () 
return 
wrapper 
return 
decorator

Cela décorerait alors la black_knightfonction de telle sorte que la couleur, "Blue", soit imprimée avant l' black_knightexécution de la fonction. La fermeture garantit que l'argument de couleur est accessible à la fonction wrapper la plus interne même lorsqu'il est renvoyé et sort de la portée, ce qui permet aux décorateurs de fonctionner.

Malgré leur nom, les décorateurs Python ne sont pas une implémentation du modèle de décorateur . Le modèle de décorateur est un modèle de conception utilisé dans les langages de programmation orientés objet à typage statique pour permettre l'ajout de fonctionnalités aux objets au moment de l'exécution. Les décorateurs Python ajoutent des fonctionnalités aux fonctions et aux méthodes au moment de la définition, et sont donc une construction de niveau supérieur aux classes à modèle de décorateur. Le modèle de décorateur lui-même est facilement implémentable en Python, car le langage est typé duck , et n'est donc généralement pas considéré comme tel.

Oeufs de Pâques

Les utilisateurs de langages à accolades , tels que C ou Java , s'attendent parfois ou souhaitent que Python suive une convention de délimiteur de blocs. La syntaxe de bloc délimitée par des accolades a été demandée à plusieurs reprises et systématiquement rejetée par les développeurs principaux. L'interpréteur Python contient un easter egg qui résume les sentiments de ses développeurs sur cette question. Le code from __future__ import braceslève l'exception SyntaxError: not a chance. Le __future__module est normalement utilisé pour fournir des fonctionnalités des futures versions de Python.

Un autre message caché, le Zen de Python (un résumé de la philosophie de conception de Python ), s'affiche lorsque vous essayez de import this.

Le message Hello world!est imprimé lorsque l'instruction import import __hello__est utilisée. Dans Python 2.7, au lieu de cela, Hello world!il imprime Hello world....

L'importation du antigravitymodule ouvre un navigateur Web vers xkcd comic 353 qui présente une utilisation fictive humoristique d'un tel module, destinée à démontrer la facilité avec laquelle les modules Python permettent des fonctionnalités supplémentaires. Dans Python 3, ce module contient également une implémentation de l'algorithme « geohash », une référence à xkcd comic 426.

Plus d articles de Worldlex Wiki

Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

Explorer l index