Syntaxe et sémantique Python - Python syntax and semantics

La syntaxe du langage de programmation Python est l'ensemble de règles qui définit comment 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 langues.

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 langues utilisent la ponctuation . Python vise à être simple et cohérent dans la conception de sa syntaxe, encapsulé dans le mantra "Il devrait y avoir une - et de préférence une seule - manière évidente de le faire", du Zen de Python .

Ce mantra s'oppose volontairement au mantra Perl et Ruby , « il y a plus d'une façon de le faire ».

Mots clés

Python a 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
Remarques

Échancrure

Python utilise des espaces pour délimiter les blocs de flux de contrôle (en suivant la règle de 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 « au 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 pour ces langages, les programmeurs indentent conventionnellement le code dans un bloc, pour le distinguer visuellement du code environnant.

Une fonction récursive nommée , qui reçoit 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):
    if x == 0:
        bar()
    else:
        baz(x)
        foo(x - 1)

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

void foo(int x)
{
    if (x == 0) {
        bar();
    } else {
        baz(x);
        foo(x - 1);
    }
}

Python impose une convention que les programmeurs dans les langages de style ALGOL suivent souvent.

Un code incorrectement indenté pourrait être mal lu par un lecteur humain différemment de ce qu'il serait interprété par un compilateur ou un interpréteur. Cet exemple illustre une erreur introduite par une indentation incorrecte :

def foo(x):
    if x == 0:
        bar()
    else:
        baz(x)
    foo(x - 1)

Ici, contrairement à l' fooexemple ci-dessus , l'appel de fonction foo(x - 1)sur la dernière ligne est à tort indenté pour être en dehors du bloc if/ else. Cela le ferait toujours être exécuté, même lorsqu'il xvaut 0, ce qui entraînerait une récursivité sans fin .

Bien que les caractères d' espace et de tabulation soient acceptés comme formes d'indentation et que n'importe quel multiple d'espaces puisse être utilisé, les espaces sont recommandés et 4 espaces (comme dans cet article) sont recommandés et sont de loin les plus couramment utilisés. Mélanger des espaces et des tabulations sur des lignes consécutives dans le même fichier de code source n'est pas autorisé à partir de Python 3 car cela peut créer des bogues difficiles à voir, car de nombreux outils ne distinguent pas visuellement les espaces et les tabulations.

Structures de données

Étant donné que Python est un langage à typage dynamique , les valeurs Python , et non les variables, contiennent des informations de type. Cela a des implications sur de nombreux aspects du fonctionnement du langage.

Toutes les variables en Python contiennent des références à des objets, et ces références sont transmises à des fonctions ; une fonction ne peut pas changer la valeur des références de variables dans sa fonction appelante (mais voir ci-dessous pour les exceptions). Certaines personnes (dont Guido van Rossum lui-même) ont appelé ce schéma 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, comme en C/ C++ . La valeur de l'objet peut être modifiée dans la fonction appelée avec le "alias", par exemple :

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

La fonction a my_funcchangé la valeur de alistavec l'argument formel al, qui est un alias de alist. Cependant, toute tentative d'opérer sur l'alias lui-même n'aura aucun effet sur l'objet d'origine. En Python, les noms accessibles non-intimes-local 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 les booléens ), donc on peut valablement multiplier un nombre complexe par un entier (par exemple) sans transtypage explicite. Cependant, il n'y a pas de conversion implicite entre, par exemple, des nombres et des chaînes ; une chaîne est un argument invalide pour une fonction mathématique qui attend un nombre.

Types de socles

Python dispose d'un large éventail de types de données de base. Parallèlement à l' arithmétique conventionnelle des nombres entiers et à 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 de chaîne. Les chaînes en Python sont immuables , donc une opération de chaîne telle qu'une substitution de caractères , qui dans d'autres langages de programmation pourrait modifier la chaîne en place , renvoie une nouvelle chaîne en Python. Les considérations de performances poussent parfois à utiliser des techniques spéciales dans les programmes qui modifient les chaînes de manière intensive, telles que la fusion de tableaux de caractères dans des chaînes uniquement si nécessaire.

Types de collecte

L'un des aspects très utiles de Python est le concept de types de collection (ou de 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 des listes ( tableaux dynamiques ), des tuples et des chaînes. Toutes les séquences sont indexées en position ( 0 à longueur − 1 ) et toutes, sauf les 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 modifiables ; les éléments peuvent être insérés, supprimés, modifiés, ajoutés ou triés sur place .

Les mappages, d'autre part, sont des types (souvent non ordonnés) implémentés sous la forme de dictionnaires qui " mappent " un ensemble de clés immuables aux é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 sur 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 sous le capot elles sont implémentées via une fonction de hachage . Cela permet des temps de recherche beaucoup plus rapides, mais nécessite que les clés ne changent pas.

Les dictionnaires sont au cœur du fonctionnement interne de Python car ils résident au cœur de tous les objets et 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 d'ensemble telles que union , intersection , difference , symmetric difference et subset testing . Il existe deux types d'ensembles : setet frozenset, la seule différence étant qu'il setest mutable et frozensetimmuable. Les éléments d'un ensemble doivent pouvoir être hachés. Ainsi, par exemple, a frozensetpeut être un élément d'un 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 objet, même les classes. Les classes, en tant qu'objets, ont une classe, appelée leur métaclasse . Python prend également en charge l' héritage multiple et les mixins .

Le langage prend en charge une introspection approfondie 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, définir 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 a différents types de littéraux de chaîne .

Littéraux de chaîne normaux

Des 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 les guillemets 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 se faire de différentes manières : avec des "f-strings" (depuis Python 3.6), en utilisant la formatméthode ou l'ancien opérateur % string-format.

Par exemple, l'instruction Perl :

print "I just printed $num pages to the printer $printer\n"

est équivalent à l'une de ces instructions Python :

print(f"I just printed {num} pages to the printer {printer}")

print("I just printed {} pages to the printer {}".format(num, printer))
print("I just printed {0} pages to the printer {1}".format(num, printer))
print("I just printed {num} pages to the printer {printer}".format(num=num, printer=printer))

print("I just printed %s pages to the printer %s" % (num, printer))
print("I just printed %(num)s pages to the printer %(printer)s" % {"num": num, "printer": 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 les documents en Perl et Ruby .

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

print("""Dear {recipient},

I wish you to leave Sunnydale and never return.

Not Quite Love,
{sender}
""".format(sender="Buffy the Vampire Slayer", recipient="Spike"))

Cordes brutes

Enfin, tous les types de chaînes mentionnés précédemment sont des variétés " brutes " (indiquées en plaçant un r littéral avant le guillemet 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 étaient à l'origine 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. La création d'une chaîne brute contenant un chemin Windows se terminant par une barre oblique inverse nécessite une 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).

Les exemples comprennent:

>>> # A Windows path, even raw strings cannot end in a backslash
>>> r"C:\Foo\Bar\Baz\"
  File "<stdin>", line 1
    r"C:\Foo\Bar\Baz\"
                     ^
SyntaxError: EOL while scanning string literal

>>> dos_path = r"C:\Foo\Bar\Baz\ " # avoids the error by adding
>>> dos_path.rstrip()              # and removing trailing space
'C:\\Foo\\Bar\\Baz\\'

>>> quoted_dos_path = r'"{}"'.format(dos_path)
>>> quoted_dos_path
'"C:\\Foo\\Bar\\Baz\\ "'

>>> # A regular expression matching a quoted string with possible backslash quoting
>>> re.match(r'"(([^"\\]|\\.)*)"', quoted_dos_path).group(1).rstrip()
'C:\\Foo\\Bar\\Baz\\'

>>> code = 'foo(2, bar)'
>>> # Reverse the arguments in a two-arg function call
>>> re.sub(r'\(([^,]*?),([^ ,]*?)\)', r'(\2, \1)', code)
'foo(2, bar)'
>>> # Note that this won't work if either argument has parens or commas in it.

Concaténation de littéraux de chaîne adjacents

Les littéraux de chaîne (utilisant éventuellement des conventions de guillemets 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

title = "One Good Turn: " \
        'A Natural History of the Screwdriver and the Screw'

est équivalent à

title = "One Good Turn: A Natural History of the Screwdriver and the Screw"

Unicode

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

s = "Γειά" # Hello in Greek
print(s)

Nombres

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

Python a 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 intégraux. Dans les nouvelles versions de langage, 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 ("true division" via l' /opérateur, ou l'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 j, par exemple 3 + 4j.

Listes, tuples, ensembles, dictionnaires

Python prend en charge la syntaxe pour la création de types de conteneurs.

Les listes (class 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, "a dog"]

ou en utilisant la création d'objet normale

a_second_list = 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, "four"
a_tuple = (1, 2, 3, "four")

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 augmenter la lisibilité. Un tuple vide est noté ().

Les ensembles (classe set) sont des conteneurs mutables d'éléments pouvant être hachés 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 pour la 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 Collection types .

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

a_dictionary = {"key 1": "value 1", 2: 3, 4: []}

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

Les opérateurs

Arithmétique

Python inclut les opérateurs +, -, *, /("true division"), //( floor division), %( module ) et **( exponentiation ), avec leur priorité mathématique habituelle .

Dans Python 3, x / yeffectue une "vraie division", ce qui signifie qu'il renvoie toujours un flottant, même si les deux xet ysont des entiers qui se divisent de manière égale.

>>> 4 / 2
2.0

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

Dans Python 2 (et la plupart des autres langages de programmation), sauf demande explicite, x / yeffectuait une division entière , renvoyant un flottant uniquement si l'une des entrées était un flottant. Cependant, étant donné que Python est un langage typé dynamiquement, il n'était pas toujours possible de dire quelle opération était effectuée, ce qui conduisait souvent à de subtils bogues, provoquant ainsi l'introduction de l' //opérateur et le 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 insont 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. Bien qu'il ait été possible de comparer si une chaîne était supérieure ou inférieure à un nombre entier dans Python 2, cela a été considéré comme une bizarrerie de conception historique et a finalement été supprimé dans Python 3.

Les expressions de comparaison en chaîne telles que a < b < cont à peu près la signification qu'elles ont en mathématiques, plutôt que la signification inhabituelle trouvée 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 < céquivaut à a < b and b < c. Cependant, il existe une différence substantielle lorsque les expressions ont des effets secondaires. a < f(x) < bévaluera f(x)exactement une fois, tandis que a < f(x) and f(x) < bl'évaluera deux fois si la valeur de aest inférieure à f(x)et une fois dans le cas contraire.

Opérateurs logiques

Dans toutes les versions de Python, les opérateurs booléens traitent les valeurs nulles ou les valeurs vides telles que "", 0, None, 0.0, [], et {}comme false, alors qu'en général, les valeurs non vides et non nulles sont considérées 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 complets 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 déclenchera jamais une 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)évalue à 5, et (4 or 5)évalue à 4.

Programmation fonctionnelle

Comme mentionné ci-dessus, une autre force 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 de ces constructions est la compréhension de liste , qui peut être exprimée avec le format suivant :

L = [mapping_expression for element in source_list if filter_expression]

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

powers_of_two = [2**n for n in range(1, 6)]

L' algorithme Quicksort peut être exprimé avec élégance (bien qu'inefficacement) en utilisant des compréhensions de liste :

def qsort(L):
    if L == []:
        return []
    pivot = L[0]
    return (qsort([x for x in L[1:] if x < pivot]) +
            [pivot] +
            qsort([x for x in L[1:] if x >= pivot]))

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

Fonctions de première classe

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

La prise en charge limitée de Python pour les fonctions anonymes est la lambdaconstruction. Un exemple est la fonction anonyme qui carré son entrée, appelée avec l'argument de 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 le 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 derivative(f, dx):
    """Return a function that approximates the derivative of f
    using an interval of dx, which should be appropriately small.
    """
    def function(x):
        return (f(x + dx) - f(x)) / dx
    return function

La syntaxe de Python, cependant, conduit parfois les programmeurs d'autres langages à penser que les fermetures ne sont pas prises en charge. La portée de la 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 de la fermeture d'un nom à une valeur 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'il avait ; la liaison modifiée de l' bintérieur de la fonction interne ne s'est pas propagée. Le moyen de contourner cela est d'utiliser une nonlocal binstruction dans bar. Dans Python 2 (qui manque de nonlocal), la solution de contournement habituelle consiste à utiliser une valeur modifiable 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é optionnelle 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 retournerait autrement une liste à espace restreint ou à calcul intensif.

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

from itertools import count

def generate_primes(stop_at=None):
    primes = []
    for n in count(start=2):
        if stop_at is not None and n > stop_at:
            return # raises the StopIteration exception
        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 itérée un peu comme une liste :

for i in generate_primes(100):  # iterate over the primes between 0 and 100
    print(i)

for i in generate_primes():  # iterate over ALL primes indefinitely
    print(i)

La définition d'un générateur apparaît identique à celle d'une fonction, sauf que le mot yield- clé est 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 itérés. Chaque fois que la forboucle de l'exemple requiert l'élément suivant, le générateur est appelé et renvoie l'élément suivant.

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

Générateur d'expressions

Introduites dans Python 2.4, les expressions génératrices sont l'équivalent d'évaluation paresseuse des compréhensions de liste. 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.

from itertools import islice

primes_under_million = (i for i in generate_primes() if i < 1000000)
two_thousandth_prime = islice(primes_under_million, 1999, 2000).next()

La plupart de la mémoire et du temps nécessaires pour générer autant de nombres premiers ne seront pas utilisés tant que l'élément nécessaire n'aura pas été réellement accédé. Malheureusement, vous ne pouvez pas effectuer d'indexation et de découpage simples de générateurs, mais devez utiliser le module itertools ou "rouler vos propres" boucles. En revanche, une compréhension de liste est fonctionnellement équivalente, mais est gourmande dans l'exécution de tout le travail :

primes_under_million = [i for i in generate_primes(2000000) if i < 1000000]
two_thousandth_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 inférieurs à deux millions), même si la plupart des éléments ne sont jamais accessibles. La compréhension du 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 Python intégrés (dicts et ensembles) devaient être intégrés à l'aide de listes ou de générateurs :

>>> dict((n, n*n) for n in range(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 for n in range(5)]  # regular list comprehension
[0, 1, 4, 9, 16]
>>>
>>> {n*n for n in range(5)}  # set comprehension
{0, 1, 4, 9, 16}
>>>
>>> {n: n*n for n in range(5)}  # dict comprehension
{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 permet le polymorphisme , non seulement au sein d'une hiérarchie de classes mais aussi par typage de canard . N'importe quel objet peut être utilisé pour n'importe quel type, et il fonctionnera tant qu'il possède les méthodes et les attributs appropriés. Et tout en 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 a une prise en charge très limitée des variables privées utilisant la modification des noms, ce qui est rarement utilisé dans la pratique, car la dissimulation d'informations est considérée par certains comme non pythonique , en ce sens qu'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 reposent plutôt sur la politesse de l'utilisateur pour ne pas « pénétrer dans la définition ».

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

Les doctrines 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 n'essaie pas d'exiger la transparence référentielle , il offre un système d'objets mais n'exige pas de comportement POO . 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 d'appel, elle invoque réellement un appel de fonction, ce qui spam.eggs = toastpeut réellement invoquer spam.set_eggs(toast). Cela annule l'avantage pratique des fonctions d' accès et reste POO 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 de nouveau style, les objets et les types ont été unifiés, permettant le sous-classement des types. Même des types entièrement nouveaux peuvent être définis, avec un comportement personnalisé pour les opérateurs infixes. Cela permet de faire beaucoup de choses radicales syntaxiquement dans Python. Un nouvel ordre de résolution de méthode pour l'héritage multiple a également été adopté avec Python 2.3. Il est également possible d'exécuter du code personnalisé tout en accédant ou en définissant 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 la saisie de 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 compliquées 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'appeler 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. Un exemple de classe définissant certaines propriétés est :

class MyClass:
    def __init__(self):
        self._a = None

    @property
    def a(self):
        return self._a

    @a.setter  # makes the property writable
    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.

Méthodes de classe et statiques

Python permet la création de méthodes de classe et de méthodes statiques via l'utilisation des décorateurs@classmethod et . 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. @staticmethod

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. En effet, il est même possible de piéger l'exception provoquée par une erreur de syntaxe .

Le style Python appelle à 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 d'aller de l'avant et d'essayer de l'utiliser, en interceptant l'exception si l'accès est rejeté.

Les exceptions peuvent également être utilisées comme moyen plus général de transfert de contrôle non local, même lorsqu'une erreur n'est pas en cause. Par exemple, le logiciel de liste de diffusion Mailman , écrit en Python, utilise des exceptions pour sortir de la 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 le modérateur.

Les exceptions sont souvent utilisées comme alternative au ifbloc -, en particulier dans les situations de thread . Une devise couramment invoquée est EAFP, ou "Il est plus facile de demander le pardon que la permission", qui est attribuée à Grace Hopper . L'alternative, connue sous le nom de LBYL, ou "Look Before You Leap", 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 :

if hasattr(spam, 'eggs'):
    ham = spam.eggs
else:
    handle_missing_attr()

Ce deuxième échantillon suit le paradigme EAFP :

try:
    ham = spam.eggs
except 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 spama l'attribut eggs, l'exemple EAFP s'exécutera plus rapidement. Lorsque spamn'a pas l'attribut eggs(le cas "exceptionnel"), l'échantillon 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 TOCTTOU ( time-of-check-to-time-of-use ), 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 a deux façons d'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 se poursuivent jusqu'à la fin de la ligne. Les commentaires couvrant plus d'une ligne 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 autrement évaluée, mais se situe entre d'autres instructions.

Commenter un bout de code :

import sys

def getline():
    return sys.stdin.readline()  # Get one line and return it

Commenter un morceau de code avec plusieurs lignes :

def getline():
    return sys.stdin.readline()    """this function
                                      gets one line
                                      and returns it"""

Les Docstrings (chaînes de documentation), c'est-à-dire les chaînes qui se trouvent seules sans affectation en tant que première ligne en retrait dans un module, une classe, une méthode ou une fonction, définissent automatiquement leur contenu en tant qu'attribut nommé __doc__, qui est destiné à stocker une description lisible par l'homme du but, du comportement et de l'utilisation de l'objet. La helpfonction intégrée génère sa sortie en fonction d' __doc__attributs. De telles chaînes peuvent être délimitées avec "ou 'pour des chaînes à une seule ligne, ou peuvent s'étendre sur plusieurs lignes si elles sont délimitées avec """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 triples ( """) sont préférés pour les docstrings simples et multilignes.

Chaîne de documentation sur une seule ligne :

def getline():
    """Get one line from stdin and return it."""
    return sys.stdin.readline()

Docstring multiligne :

def getline():
    """Get one line
       from stdin
       and return it.
    """
    return sys.stdin.readline()

Les docstrings peuvent être aussi grandes que le programmeur le souhaite et contenir des sauts de ligne . Contrairement aux commentaires, les docstrings sont elles-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'usage normal est de donner aux autres programmeurs des informations sur la façon d'invoquer l'objet documenté dans le docstring.

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

Le module standard doctest utilise des interactions copiées 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 fonction

Les annotations de fonction (type hints) sont définies dans PEP 3107. Elles permettent d'attacher des données aux arguments et de retourner 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(item: Haulable, *vargs: PackAnimal) -> Distance

Décorateurs

Un décorateur est tout objet Python appelable utilisé pour modifier une définition de fonction, de méthode ou de classe. Un décorateur reçoit l'objet d'origine défini 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 du pur sucre syntaxique , en 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 renforcent 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 Spam sketch ) pour chaque appel :

def viking_chorus(myfunc):
    def inner_func(*args, **kwargs):
        for i in range(8):
            myfunc(*args, **kwargs)
    return inner_func

Les utilisations canoniques des décorateurs de fonctions servent à créer des méthodes de classe ou des méthodes statiques , à ajouter des attributs de fonction, à tracer , à définir des pré- et post - conditions et à synchroniser , mais peuvent être utilisées pour bien plus, y compris l' élimination de la récursivité de la queue , 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 en plaçant plusieurs sur des lignes adjacentes :

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

est équivalent à

def black_knight():
    pass
black_knight = invincible(favourite_colour("Blue")(black_knight))

ou, en utilisant des variables intermédiaires

def black_knight():
    pass
blue_decorator = favourite_colour("Blue")
decorated_by_blue = blue_decorator(black_knight)
black_knight = invincible(decorated_by_blue)

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

def favourite_colour(colour):
    def decorator(func):
        def wrapper():
            print(colour)
            func()
        return wrapper
    return decorator

Cela décorerait alors la black_knightfonction de telle sorte que la couleur, "Blue", serait imprimée avant l' black_knightexécution de la fonction. La fermeture garantit que l'argument color 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 de type 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 à celle des classes de modèle de décorateur. Le modèle de décorateur lui-même est trivialement implémentable en Python, car le langage est typé canard et n'est donc généralement pas considéré comme tel.

œufs de Pâques

Les utilisateurs de langages d'accolades , tels que C ou Java , s'attendent ou souhaitent parfois que Python suive une convention de délimiteur de bloc. La syntaxe des blocs délimités 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 of Python (un résumé de la philosophie de conception 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 Hello world!cela, il imprime Hello world....

L'importation du antigravitymodule ouvre un navigateur Web vers xkcd comic 353 qui décrit 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. En Python 3, ce module contient également une implémentation de l'algorithme "geohash", une référence à xkcd comic 426 .

Les références

Liens externes