Recherche de site Web

Expression vs instruction en Python : quelle est la différence ?


Après avoir travaillé avec Python pendant un certain temps, vous finirez par rencontrer deux termes apparemment similaires: expression et déclaration . Lorsque vous parcourez la documentation officielle ou que vous creusez via un fil lié à Python sur un forum en ligne, vous pouvez avoir l'impression que les gens utilisent ces termes de manière interchangeable. C’est souvent vrai, mais assez confus, il y a des cas où la distinction d'expression contre la déclaration devient importante.

Alors, quelle est la différence entre les expressions et les déclarations dans Python?

En bref : les expressions ont des valeurs et les déclarations provoquent des effets secondaires

Lorsque vous ouvrez le glossaire Python, vous trouverez les deux définitions suivantes:

Expression : Un élément de syntaxe qui peut être évalué à une certaine valeur. (…) (Source)

Instruction : Une instruction fait partie d'une suite (un « bloc » de code). Une instruction est soit une expression, soit l’une des nombreuses constructions avec un mot-clé, (…) (Source)

Eh bien, ce n’est pas particulièrement utile, n’est-ce pas ? Heureusement, vous pouvez résumer les faits les plus importants sur les expressions et les déclarations en trois points seulement :

  1. Toutes les instructions de Python relèvent de la large catégorie des déclarations.
  2. Selon cette définition, toutes les expressions sont également des déclarations - parfois appelées instructions d'expression.
  3. Toutes les affirmations ne sont pas des expressions.

Dans un sens technique, chaque ligne ou bloc de code est une instruction dans Python. Cela comprend expressions , qui représentent un type spécial de déclaration. Qu'est-ce qui rend une expression spéciale? Vous le découvrirez maintenant.

Expressions: déclarations avec des valeurs

Essentiellement, vous pouvez remplacer toutes les expressions de votre code par les valeurs calculées, qu'ils produiraient à l'exécution, sans modifier le comportement global de votre programme. Les déclarations, en revanche, ne peuvent pas être remplacées par des valeurs équivalentes à moins qu'elles ne soient des expressions.

Considérez l'extrait de code suivant :

>>> x = 42
>>> y = x + 8
>>> print(y)
50

Dans cet exemple, les trois lignes de code contiennent des instructions. Les deux premières sont des instructions d'affectation, tandis que la troisième est un appel à la fonction print().

Lorsque vous regardez chaque ligne de plus près, vous pouvez commencer à démonter l'instruction correspondante en sous-composants. Par exemple, l'opérateur d'affectation (=) se compose des parties à gauche et à droite. La partie à gauche du signe égal indique le nom de la variable, tel que x ou y, et la partie à droite est la valeur attribuée à cette variable.

Le mot valeur est la clé ici. Notez que la variable x se voit attribuer une valeur littérale, 42, qui est intégrée directement dans votre code. En revanche, la ligne suivante attribue une expression arithmétique, x + 8, à la variable y. Python doit d'abord calculer ou évaluer une telle expression pour déterminer la valeur finale de la variable lorsque votre programme est en cours d'exécution.

Les expressions arithmétiques ne sont qu'un exemple d'expressions python. D'autres incluent des expressions logiques, des expressions conditionnelles, etc. Ce qu'ils ont tous en commun, c'est une valeur à laquelle ils évaluent, bien que chaque valeur soit généralement différente. En conséquence, vous pouvez remplacer en toute sécurité toute expression par la valeur correspondante:

>>> x = 42
>>> y = 50
>>> print(y)
50

Ce programme court donne le même résultat qu'avant et est fonctionnellement identique à celui précédent. Vous avez calculé l'expression arithmétique à la main et inséré la valeur résultante à sa place.

Notez que vous pouvez évaluer x + 8, mais vous ne pouvez pas faire de même avec l'affectation y=x + 8, même si elle incorpore une expression. La ligne entière de code représente une instruction pure sans valeur intrinsèque. Alors, à quoi ça sert de telles déclarations ? Il est temps de plonger dans les instructions Python et de le découvrir.

Déclarations: instructions avec effets secondaires

Les instructions qui ne sont pas des expressions provoquent des effets secondaires, qui modifient l'état de votre programme ou affectent une ressource externe, telle qu'un fichier sur le disque. Par exemple, lorsque vous attribuez une valeur à une variable, vous définissez ou redéfinissez cette variable quelque part dans la mémoire de Python. De même, lorsque vous appelez print(), vous écrivez effectivement dans le flux de sortie standard (stdout), qui, par défaut, affiche le texte à l'écran.

D'accord. Vous avez couvert des déclarations qui sont des expressions et des déclarations qui ne sont pas des expressions. À partir de maintenant, vous pouvez les appeler expressions pures et instructions pures , respectivement. Mais il s'avère qu'il y a un terrain d'entente ici.

Certaines instructions peuvent avoir une valeur et provoquer des effets secondaires en même temps. En d'autres termes, ce sont des expressions avec des effets secondaires ou, de manière équivalente, des déclarations avec une valeur. Un excellent exemple de cela serait la fonction Next() de Python intégrée dans la langue:

>>> fruit = iter(["apple", "banana", "orange"])
>>> next(fruit)
'apple'
>>> next(fruit)
'banana'
>>> next(fruit)
'orange'

Ici, vous définissez un objet itérateur nommé fruit, qui vous permet de parcourir une liste de noms de fruits. Chaque fois que vous appelez next() sur cet objet, vous modifiez son état interne en déplaçant l'itérateur vers l'élément suivant de la liste. C'est votre effet secondaire. Simultanément, next() renvoie le nom du fruit correspondant, qui est la partie valeur de l'équation.

En général, il est considéré comme une bonne pratique de ne pas mélanger des valeurs avec des effets secondaires dans une seule expression. La programmation fonctionnelle encourage l'utilisation de fonctions pures, tandis que les langages procéduraux font la distinction entre les fonctions qui renvoient une valeur et les procédures qui ne le font pas.

Enfin, bien que cela puisse sembler contre-intuitif au début, Python a une déclaration qui n'évalue à rien ni ne provoque d'effets secondaires. Pouvez-vous deviner ce que c'est et quand vous souhaitez l'utiliser? Pour vous donner un petit indice, il est communément connu comme un non-opération en informatique, qui est court pour pas d'opération .

Devinez quoi? C'est l'instruction Python pass ! Vous l’utilisez souvent comme espace réservé aux endroits où une instruction est syntaxiquement requise, mais vous ne souhaitez entreprendre aucune action. Vous pouvez utiliser ces espaces réservés dans des définitions de fonctions vides ou des boucles lors de vos premières étapes de développement. Notez que même si l'Ellipsis (...) de Python peut avoir un objectif similaire, il a une valeur, faisant de l'Ellipsis une expression.

Résumé des expressions et des déclarations

Pour ramener le point à la maison, jetez un œil rapide au diagramme suivant. Cela vous aidera à mieux comprendre les différents types d'expressions et de déclarations dans Python:

En résumé, une instruction peut être n'importe quelle instruction Python, qu'il s'agisse d'une seule ligne ou d'un bloc de code. Les quatre quadrants du diagramme ci-dessus représentent des déclarations. Cependant, ce terme est souvent utilisé pour désigner instructions pures qui ne provoquent que des effets secondaires sans avoir de valeur.

En revanche, une expression pure est un type spécial d'instruction qui n'évalue qu'une certaine valeur sans provoquer d'effets secondaires. De plus, vous pouvez rencontrer des expressions avec effets secondaires, qui sont des déclarations avec une valeur. Ce sont des expressions qui produisent une valeur tout en provoquant des effets secondaires. Enfin, un no-op est une déclaration qui ne fait ni l’un ni l’autre : elle ne produit aucune valeur et ne provoque aucun effet secondaire.

Ensuite, vous apprendrez à les reconnaître dans la nature.

Comment vérifier si une instruction Python est une expression ou une instruction ?

À ce stade, vous savez déjà que chaque instruction Python est techniquement toujours une déclaration. Donc, une question plus spécifique que vous voudrez peut-être vous poser est de savoir si vous avez affaire à une expression ou à une instruction pure . Vous répondez à cette question à deux: manuellement puis par programme à l'aide de Python.

Vérifier manuellement dans le Python REP

Pour déterminer rapidement la réponse à la question posée dans cette section, vous pouvez tirer parti de la puissance de Python REPL, qui évalue les expressions au fur et à mesure que vous les tapez. Si une instruction se révèle être une expression, vous verrez immédiatement la représentation sous forme de chaîne par défaut de sa valeur dans la sortie :

>>> 42 + 8
50

Il s'agit d'une expression arithmétique dont le résultat est 50. Puisque vous n’avez pas intercepté sa valeur, par exemple en affectant l’expression à une variable ou en la passant à une fonction comme argument, Python affiche le résultat calculé pour vous. Si la même ligne de code avait été présente dans un script Python, l’interpréteur aurait ignoré la valeur évaluée, qui aurait été perdue.

En revanche, l'exécution d'une instruction pure dans le REP ne montre rien dans la sortie:

>>> import math
>>>

Il s'agit d'une instruction import, qui n'a pas de valeur correspondante. Au lieu de cela, il charge le package Python spécifié dans votre espace de noms actuel comme effet secondaire.

Cependant, vous devez être prudent avec cette approche. Parfois, la représentation de chaîne par défaut d'une valeur calculée peut être trompeuse. Considérez cet exemple:

>>> fruit = {"name": "apple", "color": "red"}
>>> fruit.get("taste")
>>> fruit.get("color")
'red'

Vous définissez un dictionnaire Python qui représente un fruit. La première fois que vous appelez .get() dessus, il n'y a pas de sortie visible. Mais ensuite, vous appelez .get() à nouveau avec une clé différente, et il renvoie la valeur associée à cette clé, qui est 'Red' .

Dans le premier cas, .get() renvoie Aucun pour indiquer une paire de valeurs de clé manquante car le dictionnaire ne contient pas la clé "Taste" . Cependant, lorsque vous utilisez une clé qui existe, comme "couleur" , la méthode renvoie la valeur correspondante.

Le Python REPL n'affiche jamais Aucun à moins d'être explicitement imprimé, ce qui peut parfois prêter à confusion si vous n'êtes pas au courant de ce comportement. En cas de doute, vous pouvez toujours appeler print() sur le résultat pour révéler sa vraie valeur :

>>> fruit.get("taste")
>>> print(fruit.get("taste"))
None

Bien que cela fonctionne, il existe un moyen plus fiable de vérifier si une instruction est une expression en Python.

Vous pouvez attribuer une instruction à une variable afin de vérifier s'il s'agit d'une valeur r. Sinon, s’il s’agit d’une instruction pure, vous ne pourrez pas l’utiliser comme valeur pour votre variable et vous obtiendrez cette erreur :

>>> x = import math
  File "<python-input-0>", line 1
    x = import math
        ^^^^^^
SyntaxError: invalid syntax

Python soulève une syntaxerror pour vous dire que vous ne pouvez pas attribuer une instruction import dans une variable car cette instruction n'a pas de valeur tangible.

Un cas quelque peu spécial est l'affectation en chaîne, qui pourrait initialement sembler comme si vous essayiez d'attribuer y=42 à la variable x pour vérifier s'il s'agit d'une expression:

>>> x = y = 42

Cependant, cela fait plusieurs variables - x et y dans ce cas - référer à la même valeur, 42 . Il s'agit d'une notation raccourci pour effectuer deux affectations distinctes, x=42 et y=42 .

Une autre façon de savoir si un morceau de code Python est une expression ou une instruction pure implique de l'envelopper dans parenthèses . Les parenthèses vous aident généralement des termes de groupe pour modifier l'ordre d'opérations par défaut déterminé par la priorité de l'opérateur. Mais vous ne pouvez envelopper que des expressions, pas des déclarations:

>>> (2 + 2)
4

>>> (x = 42)
  File "<python-input-7>", line 1
    (x = 42)
     ^^^^^^
SyntaxError: invalid syntax. Maybe you meant '==' or ':=' instead of '='?

Ligne 1 contient une expression, qui évalue à quatre, tandis que la ligne 4 conduit à une erreur de syntaxe car Python essaie sans succès d'évaluer une instruction d'affectation. Le message d'erreur affiché vous invite à modifier l'opérateur d'attribution (=) en l'égalité de valeur ( == ) ou à l'opérateur de morse (:=).

Jusqu’à présent, vous avez comparé manuellement des expressions et des instructions dans Python REPL. Dans la section suivante, vous apprendrez comment procéder par programmation, ce qui peut s’avérer utile s’il s’agit d’une tâche répétitive que vous souhaitez automatiser.

Construire un détecteur d'expression et d'instruction Python

Pour déterminer si une instruction est une expression ou une instruction par programme, vous pouvez appeler les fonctions intégrées eval() et exec() de Python. La première fonction vous permet d'évaluer des expressions de manière dynamique, et la seconde peut exécuter du code arbitraire à partir d'une chaîne :

>>> eval("2 + 2")
4
>>> exec("x = 42")
>>> print(x)
42

La chaîne "2 + 2" contient une expression Python que eval() évalue à l'entier 4. Vous pouvez affecter la valeur renvoyée par eval() à une variable si vous le souhaitez. D'un autre côté, exec() ne renvoie rien mais provoque des effets secondaires. Remarquez comment vous pouvez accéder à la variable définie dans la chaîne passée à exec() après avoir appelé cette fonction dans la même portée.

Les deux fonctions rapportent une erreur de syntaxe lorsque la chaîne d'entrée n'est pas une expression ou une instruction valide:

>>> eval("2 +")
Traceback (most recent call last):
  ...
SyntaxError: invalid syntax

>>> exec("x = 2 +")
Traceback (most recent call last):
  ...
SyntaxError: invalid syntax

De plus, eval() génère une erreur de syntaxe lorsque la chaîne fournie contient une instruction pure sans valeur. Simultanément, la même instruction fonctionne très bien avec exec() :

>>> eval("x = 2 + 2")
Traceback (most recent call last):
  ...
SyntaxError: invalid syntax

>>> exec("x = 2 + 2")

Vous pouvez profiter de cet écart pour différencier les expressions et les déclarations pures. Mais n'oubliez pas d'appeler eval() avant exec() pour éviter les faux positifs. Étant donné que toutes les expressions sont des instructions, exec() réussira presque toujours s'il reçoit une expression ou une déclaration comme argument:

>>> eval("print('Hello, World!')")
Hello, World!

>>> exec("print('Hello, World!')")
Hello, World!

Les deux fonctions vous donnent un résultat identique : Hello World !. Si vous appelez exec() et que vous vous arrêtez là, vous pourriez conclure à tort que le code est une instruction alors qu'il pourrait s'agir d'une expression valide. Pendant ce temps, tous les appels de fonction en Python sont techniquement des expressions et doivent être classés comme tels. Voici comment résoudre ce problème.

Pour éviter la duplication de code, vous pouvez combiner evalent() et exec() en une seule fonction, qui délègue à ast.parse() avec le mode approprié:

>>> import ast

>>> def valid(code, mode):
...     try:
...         ast.parse(code, mode=mode)
...         return True
...     except SyntaxError:
...         return False
...

>>> valid("x = 2 + 2", mode="eval")
False

>>> valid("x = 2 + 2", mode="exec")
True

Cette fonction accepte un paramètre Python Code et le paramètre mode , qui peut prendre l'une des deux valeurs: "eval" ou "Exec ". La fonction renvoie false lorsque l'expression ou l'instruction sous-jacente est syntaxiquement incorrecte. Sinon, il renvoie true lorsque le code s'exécute avec succès dans le mode spécifié.

Pour couronner le tout, vous pouvez écrire une autre fonction d'assistance qui fournira une représentation textuelle pour l'extrait de code donné:

>>> def describe(code):
...     if valid(code, mode="eval"):
...         return "expression"
...     elif valid(code, mode="exec"):
...         return "statement"
...     else:
...         return "invalid"
...

>>> describe("x = 2 + 2")
'statement'

>>> describe("2 + 2")
'expression'

>>> describe("2 +")
'invalid'

Vous vérifiez d'abord si le code donné est une expression. Quand c'est le cas, vous retournez la chaîne "Expression" . Sinon, vous pouvez vérifier si le code est une instruction. S'il se qualifie comme une instruction, vous renvoyez la chaîne "Instruction" . Enfin, si aucune condition n'est remplie, vous concluez que le code est "invalide" .

Cette approche peut s'avérer utile lorsque vous devez effectuer un tel test par programmation pour une raison quelconque. Peut-être que vous créez votre propre Python REPL en utilisant la correspondance de modèles structurels en Python, et vous devez décider quand afficher une expression évaluée.

À l'heure actuelle, vous devez comprendre plus intuitivement la différence entre les expressions et les déclarations dans Python. Vous devriez également être en mesure de dire laquelle est laquelle. La prochaine question importante est de savoir s'il s'agit simplement d'une distinction sémantique pour les puristes ou si elle a une signification dans votre pratique de codage quotidienne. Vous allez le découvrir maintenant!

Cette différence est-elle importante dans votre programmation quotidienne ?

La plupart du temps, vous n'avez pas trop de réfléchir à la question de savoir si vous travaillez avec une expression ou une déclaration dans Python. Cela dit, il y a deux exceptions notables qui méritent d'être mentionnées:

  1. expressions lambda
  2. Instructions assert

Vous commencerez par la première, qui est l'expression lambda.

Expression lambda

Le mot-clé lambda de Python vous permet de définir une fonction anonyme, qui peut être utile pour des opérations ponctuelles comme la spécification de la clé de tri ou d'une condition sur laquelle filtrer :

>>> fruits = [("apple", 2), ("banana", 0), ("orange", 3)]

>>> sorted(fruits, key=lambda item: item[1])
[('banana', 0), ('apple', 2), ('orange', 3)]

>>> list(filter(lambda item: item[1] > 0, fruits))
[('apple', 2), ('orange', 3)]

Ici, vous définissez une liste de tuples à deux éléments contenant le nom d'un fruit et sa quantité respective. Ensuite, vous triez cette liste par ordre croissant en fonction de la quantité. Enfin, vous filtrez la liste, ne laissant que les fruits qui ont au moins une unité.

Il s'agit d'une approche pratique tant que vous n'avez pas besoin de référencer de telles fonctions en ligne au-delà de leur utilisation immédiate. Bien que vous puissiez toujours attribuer une expression lambda à une variable pour une utilisation ultérieure, il n'est pas syntaxiquement équivalent à une fonction régulière définie avec def .

Une définition de fonction démarre un nouveau bloc de code représentant le corps de la fonction. En Python, pratiquement chaque bloc de code appartient à une instruction composée et ne peut donc pas être évalué.

Étant donné que les instructions n'ont pas de valeurs, vous ne pouvez pas attribuer une définition de fonction à une variable comme vous le pouvez avec une expression lambda :

>>> inline = lambda: 42

>>> regular = (
...     def function():
...         return 42
... )
...
  File "<python-input-1>", line 2
    def function():
    ^^^
SyntaxError: invalid syntax

La variable inline contient une référence à une fonction anonyme définie comme un lambda, alors que regular est une tentative infructueuse d'attribuer une définition de fonction nommée à un variable.

Cependant, vous êtes autorisé à attribuer une référence à une fonction qui a déjà été définie ailleurs :

>>> def function():
...     return 42
...
>>> alias = function

Notez la signification différente de l'instruction def function(): et de la référence function qui apparaît en dessous. Le premier est le plan de ce que fait la fonction, et le second est l’adresse de la fonction, que vous pouvez transmettre sans réellement appeler la fonction.

Une fonction définie avec le mot-clé lambda doit toujours contenir exactement une expression dans son corps. Il sera évalué et retourné implicitement sans que vous ayez à inclure l'instruction return . En fait, l'utilisation d'instructions dans les fonctions lambda est carrément interdite. Si vous essayez de les utiliser, vous provoquerez une erreur de syntaxe:

>>> lambda: pass
  File "<python-input-0>", line 1
    lambda: pass
            ^^^^
SyntaxError: invalid syntax

Vous avez appris que pass est une instruction, elle n'a donc pas sa place dans une expression lambda. Cependant, il existe une solution à ce problème. Vous pouvez toujours envelopper une ou plusieurs instructions dans une fonction et appeler cette fonction dans votre expression lambda, comme ceci :

>>> import tkinter as tk

>>> def on_click(age):
...     if age > 18:
...         print("You're an adult.")
...     else:
...         print("You're a minor.")
...

>>> window = tk.Tk()
>>> button = tk.Button(window, text="Click", command=lambda: on_click(42))
>>> button.pack(padx=10, pady=10)
>>> window.mainloop()

Il s'agit d'une application Python minimale avec une interface utilisateur graphique (GUI) construite avec Tkinter. La ligne en surbrillance enregistre un auditeur de l'événement de clic du bouton. Le gestionnaire d'événements est défini comme une expression de Lambda en ligne, qui appelle une fonction de wrapper qui résume une instruction conditionnelle. Vous ne pouviez pas exprimer une logique aussi complexe uniquement avec une expression de lambda.

Déclaration d'affirmation

Quant à l'instruction assert, il existe un point commun de confusion qui l'entoure, qui découle de sa syntaxe quelque peu trompeuse. Il est trop facile d'oublier que assert est une instruction et ne se comporte pas comme un appel de fonction ordinaire. Cela peut parfois provoquer un comportement involontaire lorsque vous n’y faites pas attention.

Dans sa forme la plus basique, le mot-clé assert doit être suivi d'un prédicat logique ou d'une expression qui correspond à une valeur booléenne :

>>> assert 18 < int(input("What's your age? "))
What's your age? 42

>>> assert 18 < int(input("What's your age? "))
What's your age? 15
Traceback (most recent call last):
  ...
AssertionError

Si l'expression devient la vérité, alors rien ne se passe. Si l'expression est Falsy, Python augmente une AssertionError , à condition que vous n'ayez pas désactivé les affirmations avec une option de ligne de commande ou une variable d'environnement.

Vous pouvez éventuellement ajouter un message d'erreur personnalisé à afficher avec l'erreur d'assertion générée. Pour ce faire, placez une virgule après le prédicat et incluez une chaîne littérale ou toute expression qui vaut un :

>>> assert 18 < int(input("What's your age? ")), "You're a minor"
What's your age? 15
Traceback (most recent call last):
  ...
AssertionError: You're a minor

Jusqu'ici, tout va bien. Mais l’incorporation d’un message d’erreur plus long peut conduire à un code moins lisible, vous incitant à rompre cette ligne d’une manière ou d’une autre.

Python offre une fonctionnalité intéressante appelée continuation de ligne implicite, qui vous permet de diviser une longue instruction sur plusieurs lignes sans utiliser de barre oblique inverse explicite (\). Vous pouvez y parvenir en plaçant vos expressions entre parenthèses, crochets ou accolades, ce qui rend le code plus organisé et plus facile à lire :

>>> assert (
...     18 < int(input("What's your age? ")),
...     "You're a minor"
... )
<python-input-0>:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (
What's your age? 15
>>>

Cela fonctionne généralement, mais pas dans ce cas. Comme vous pouvez le constater, les versions modernes de Python émettent même un avertissement pour vous informer que quelque chose ne va probablement pas.

N'oubliez pas qu'une instruction affirmer s'attend à une expression juste après le mot-clé. Lorsque vous entourez votre message de prédicat et personnalisé avec des parenthèses, vous définissez essentiellement un tuple, qui est traité comme une seule expression. Python évalue ces séquences non vides à true . Ainsi, votre instruction affirmer passera toujours, quelle que soit la condition réelle que vous essayez de vérifier.

Pour éviter ce problème, vous feriez mieux d'utiliser une continuation de ligne explicite lorsque vous souhaitez briser une longue ligne:

>>> assert 18 < int(input("What's your age? ")), \
...     "You're a minor"
What's your age? 15
Traceback (most recent call last):
  ...
AssertionError: You're a minor

Maintenant, vous observez à nouveau le comportement attendu car le prédicat est évalué correctement.

Plus tôt, vous avez appris que les blocs de code en Python font partie d'instructions composées. Ensuite, vous explorerez les différences entre les instructions simples et composées en Python.

Que sont les instructions simples et composées en Python ?

Les instructions sont les éléments de base de vos programmes Python. Ils vous permettent de contrôler le flux d'exécution et d'effectuer des actions, telles que l'attribution de valeurs aux variables. Vous pouvez classer les déclarations en deux types principaux :

  1. Déclarations simples
  2. Déclarations composées

Des instructions simples peuvent s'adapter sur une seule ligne, tandis que les instructions composées comprennent d'autres instructions qui s'étendent généralement à plusieurs lignes de code en retrait suivis d'un caractère Newline.

Le tableau ci-dessous présente quelques exemples courants des deux types de déclarations :

assert age > 18

si âge > 18 : ...

import math

tandis que vrai: ...

return 42

pour _ dans la plage(3) : ...

pass

essayez : ...

x = 42 + 8

def add(x, y): ...

Comme vous pouvez le voir, les instructions simples provoquent un effet secondaire particulier - sauf l'instruction passage , ce qui ne le fait pas. Les instructions composées, en revanche, incluent les constructions de flux de contrôle traditionnelles comme les boucles, les conditions et les définitions de fonction.

Lorsque vous regardez de plus près les instructions composées, vous constaterez qu'elles se composent d'une ou plusieurs clauses, chacune contenant un en-tête et une suite. Prenons l'exemple de l'instruction conditionnelle suivante :

if age > 18:
    print("Welcome!")
elif age < 18:
    raise ValueError("You're too young to be here.")
else:
    import sys
    sys.exit(1)

Les lignes en surbrillance représentent les en-têtes de clause de la déclaration composée, tandis que les lignes restantes représentent leurs suites correspondantes. Par exemple, la clause IF vérifie si l'âge est supérieur à dix-huit ans et appelle conditionnellement la fonction print() dans sa suite.

Tous les en-têtes de clause dans une instruction composée sont alignés au même niveau d'indentation. Ils commencent par un mot-clé Python, tel que si , elif , ou else , et concluent par un colon (: ) Cela marque le début d'un bloc de code de la suite. Une suite est une collection de déclarations régies par sa clause respective. Dans ce cas, les trois clauses déterminent lesquelles des suites à exécuter en fonction de la condition d'âge.

Il existe un type spécial de déclaration composée connue sous le nom de Liste des instructions , qui consiste en une séquence de déclarations simples. Bien que chacun d'eux doit être placé sur une seule ligne, vous pouvez presser plusieurs instructions simples en une seule ligne. Savez-vous comment?

Comment mettre plusieurs instructions sur une seule ligne?

Dans la plupart des cas, il est préférable de ne placer qu’une seule instruction ou expression par ligne pour des raisons de lisibilité. Cependant, si vous insistez pour en placer plusieurs sur la même ligne, vous pouvez utiliser le point-virgule (;) comme séparateur d'instructions.

Un cas d'utilisation courant où cela pourrait être utile consiste à exécuter un programme monoligne dans la ligne de commande en utilisant l'option python -c :

$ python -c 'import sys; print(sys.version)'
3.13.0 (main, Oct 19 2024, 15:05:58) [GCC 13.2.0]

Cela vous permet de tester rapidement des idées sous forme d'extraits de code court. Mais ces jours-ci, la plupart des terminaux vous permettent de diffuser le code sur plusieurs lignes sans aucun problème:

$ python -c '
> import sys
> print(sys.version)
> '
3.13.0 (main, Oct 19 2024, 15:05:58) [GCC 13.2.0]

La commande que l'interprète Python attend peut consister en plusieurs lignes.

Un autre cas d'utilisation courant du point-virgule consiste à insérer un point d'arrêt dans votre code. Avant Python 3.7, qui introduisait la fonction intégrée breakpoint(), vous sautiez dans le débogueur avec la ligne de code idiomatique suivante :

import pdb; pdb.set_trace()

Lorsque l'interpréteur clique sur pdb.set_trace(), il suspend l'exécution et vous entrez dans la session de débogage interactive à l'aide du débogueur Python (pdb).

De plus, vous pouvez essayer d'utiliser cette astuce pour obscurcir intentionnellement ou réduire votre code Python dans une certaine mesure. Cependant, vous ne pourrez pas échapper à certaines limitations syntaxiques, vous obtiendrez donc de meilleurs résultats avec des outils externes comme Pyarmor.

Avant de fermer ce tutoriel, vous devez répondre à une dernière question sur les expressions et les déclarations dans Python. Celui qui vous donnera la base pour relever des défis de programmation plus avancés.

Les déclarations peuvent-elles avoir une double nature en Python ?

Puisque toutes les instructions en Python sont des instructions, vous pouvez exécuter une expression qui a des effets secondaires sans tenir compte de la valeur calculée. C'est souvent le cas lorsque vous appelez une fonction ou une méthode mais ignorez sa valeur de retour :

>>> with open("file.txt", mode="w", encoding="utf-8") as file:
...     file.write("Hello, World!")
...
13

Dans l'exemple ci-dessus, vous appelez la méthode .write() d'un objet de fichier avec le string "Bonjour, monde!" comme argument. Bien que cette méthode renvoie le nombre de caractères écrits, qui est treize, vous les ignorez en n'interceptant en aucune façon la valeur renvoyée dans une variable ou en la traitant davantage. Remarquez, cependant, que le Python Rela affiche automatiquement le résultat de la dernière expression évaluée dans cette situation.

D'accord. Vous pouvez donc profiter des expressions uniquement pour leurs effets secondaires si elles en ont. Mais qu’en est-il de l’inverse ? Pouvez-vous évaluer les déclarations ? Vous êtes sur le point de le découvrir !

Affectations

Certains langages de programmation brouillent les frontières entre les instructions et les expressions. Par exemple, vous pouvez évaluer une instruction d'affectation en C ou C++ :

#include <stdio.h>

int main() {
    int x;
    while (x = fgetc(stdin)) {
        if (x == EOF)
            break;
        putchar(x);
    }
    return 0;
}

Ce programme court fait écho à tout ce que l'utilisateur tape sur le clavier. Notez la ligne en surbrillance, qui récupère le caractère suivant de l'entrée standard et l'affecte à une variable locale, x. Simultanément, cette affectation est évaluée comme une expression booléenne et utilisée comme condition de continuation pour la boucle while. En d’autres termes, tant que le numéro ordinal du caractère saisi est différent de zéro, la boucle continue.

Il s'agit d'une source notoire de bugs qui a tourmenté les programmes C et C ++ depuis des lustres. Les programmeurs utilisaient souvent par erreur l'opérateur d'attribution (=) dans de telles conditions au lieu de l'opérateur de test d'égalité prévu ( == ) en raison de leur ressemblance visuelle. Bien que l'exemple ci-dessus utilise intentionnellement cette fonctionnalité, c'était généralement le résultat d'une erreur humaine qui pourrait conduire à un comportement inattendu.

Pendant longtemps, les développeurs de base ont éloigné de la mise en œuvre d'une fonctionnalité similaire dans Python en raison de préoccupations concernant cette confusion potentielle. C'était le cas jusqu'à Python 3.8, qui a introduit l'opérateur de morse (:=) pour permettre les expressions d'affectation :

MAX_BYTES = 1024

buffer = bytearray()
with open("audio.wav", mode="rb") as file:
    while chunk := file.read(MAX_BYTES):
        buffer.extend(chunk)

Dans cet extrait de code, vous lisez progressivement un fichier WAV dans des morceaux binaires jusqu'à trébucher sur l'octet de contrôle de fin de fichier, qui est indiqué par un morceau vide. Cependant, au lieu de vérifier explicitement si le morceau renvoyé par .read() est non exact - ou s'il évalue à true - vous leviez l'expression de l'affectation pour exécuter et évaluer l'affectation en une étape.

Cela vous évite quelques lignes de code, qui autrement auraient ressemblé à ceci :

MAX_BYTES = 1024

buffer = bytearray()
with open("audio.wav", mode="rb") as file:
    while True:
        chunk = file.read(MAX_BYTES)
        if not chunk:
            break
        buffer.extend(chunk)

Vous avez transformé une boucle déterministe en une infinie et ajouté trois autres instructions: l'instruction Affectation, l'instruction conditionnelle et Break .

Contrairement à l'exemple C, il n'y a aucun moyen de confondre l'expression de l'affectation avec son homologue de déclaration. Python continue d'interdire les instructions d'attribution dans un contexte booléen. À de telles occasions, vous devez explicitement utiliser l'opérateur de morse, qui s'aligne bien avec l'un des aphorismes du zen de Python:

Explicite est meilleur qu'implicite. (Source)

Il existe d'autres exemples d'énoncés qui ont leurs analogues d'expression dans Python. Ensuite, vous examinerez les déclarations et expressions conditionnelles.

Conditions

De nombreux langages de programmation fournissent un opérateur conditionnel ternaire, qui combine trois éléments : une condition logique, une valeur si la condition est évaluée à Vrai et une valeur alternative si la condition est évaluée à Faux.

Dans les langues de la famille C, l'opérateur ternaire (?:) ressemble à l'émoticône d'Elvis Presley avec sa coiffure distinctive. Alors que ces langages l’appellent l’opérateur Elvis, Python s’en tient au terme expression conditionnelle plus conservateur. Voici à quoi cela ressemble :

>>> def describe(users):
...     if not users:
...         print("No people")
...     else:
...         print(len(users), "people" if len(users) > 1 else "person")
...

>>> describe([])
No people

>>> describe(["Alice"])
1 person

>>> describe(["Alice", "Bob"])
2 people

À première vue, l'expression conditionnelle de Python ressemble à l'instruction conditionnelle standard condensée en une seule ligne. Il commence par une expression associée à une valeur de vérité, suivie de la condition à vérifier, puis de l'expression correspondant à une valeur de fausset. Il vous permet d'évaluer la logique conditionnelle, qui nécessiterait normalement l'utilisation d'une instruction conditionnelle.

Compréhensions

Un autre exemple de déclarations occupant la zone grise est toutes sortes d'expressions de compréhension , comme la compréhension de la liste ou l'expression du générateur, dont vous pouvez profiter pour éviter les boucles explicites:

>>> fruits = [("apple", 2), ("banana", 0), ("orange", 3)]
>>> sorted(name.title() for name, quantity in fruits if quantity > 0)
['Apple', 'Orange']

Encore une fois, la syntaxe est similaire à la boucle for et à l'instruction if, mais vous les réduisez en une seule ligne et réorganisez leurs éléments individuels. Cela ne fonctionne bien que lorsque la condition et l'expression à évaluer sont raisonnablement petites pour que vous puissiez les insérer sur une ligne ou deux. Sinon, vous vous retrouverez avec un morceau de code encombré et difficile à lire.

Enfin, les générateurs Python méritent d’être mentionnés ici car ils utilisent une syntaxe qui peut être à la fois une expression et une instruction.

Générateurs

Le rendement et le rendement de des mots clés apparaissent à l'intérieur des fonctions du générateur, qui vous permettent de gérer efficacement de grands flux de données ou de définir les coroutines.

Vous laissez Python exécuter yield comme une instruction lorsque vous souhaitez produire des valeurs à partir d'une fonction génératrice :

>>> import random

>>> def generate_noise(size):
...     for _ in range(size):
...         yield 2 * random.random() - 1
...

>>> list(generate_noise(3))
[0.7580438973021972, 0.5273057193944659, -0.3041263216813208]

Ce générateur est un producteur de valeurs aléatoires comprises entre -1 et 1. Utiliser yield ici est quelque peu similaire à l'utilisation de l'instruction return. Elle transmet une série de valeurs à l'appelant comme effet secondaire, mais au lieu de terminer complètement la fonction, l'instruction yield suspend l'exécution de la fonction, lui permettant de reprendre et de continuer plus tard.

Désormais, vous pouvez éventuellement évaluer le rendement en tant qu'expression pour transformer votre générateur en un prosommateur (producteur et consommateur) avec potentiellement de nombreux points d'entrée et de sortie. Chaque expression yield peut fournir et recevoir des valeurs dans les deux sens. Les valeurs générées représentent la sortie du générateur, tandis que les valeurs renvoyées dans le générateur constituent l'entrée :

>>> def lowpass_filter():
...     a = yield
...     b = yield a
...     while True:
...         a, b = b, (yield (a + b) / 2)
...

La première expression rendement ne donne implicitement rien ( non ) mais reçoit une valeur du monde extérieur lorsqu'il est évalué comme une expression. Cette valeur est ensuite attribuée à une variable locale nommée a .

La deuxième expression rendement donne a , tout en stockant simultanément une autre valeur dans b . À ce stade, le filtre devient saturé et prêt à traiter les données entrantes. La possibilité de consommer et de produire des valeurs à chaque point de suspension ( rendement ) permet de pousser un signal via votre filtre comme un courant électrique traverse un circuit.

La dernière expression yield calcule et donne la moyenne des deux nombres, écrase a par b, puis attribue une nouvelle valeur à b .

Vous pouvez connecter les deux générateurs l'un à l'autre en appelant .send() sur votre filtre passe-bas, qui calcule une moyenne mobile sur deux points qui lisse le signal :

>>> lf = lowpass_filter()
>>> lf.send(None)
>>> for value in generate_noise(10):
...     print(f"{value:>5.2f}: {lf.send(value):>5.2f}")
...
 0.66:  0.66
-0.01:  0.32
 0.30:  0.15
-0.10:  0.10
-0.98: -0.54
 0.79: -0.10
 0.31:  0.55
-0.10:  0.11
-0.25: -0.18
 0.57:  0.16

Les nombres dans la colonne de gauche proviennent directement du générateur de bruit et sont transmis directement au filtre passe-bas. La colonne de droite contient la sortie de ce filtre. Notez que vous devez d'abord amorcer votre prosommateur en lui envoyant None ou en appelant next() afin qu'il passe à la première expression yield.

Conclusion

Dans ce didacticiel, vous avez exploré les différences fondamentales entre expressions et instructions dans Python. Vous avez appris que les expressions sont des morceaux de syntaxe qui évaluent une valeur , tandis que les instructions sont des instructions qui peuvent provoquer des effets secondaires . En même temps, il y a une zone grise entre eux.

Comprendre cette distinction est crucial pour les développeurs Python, car elle affecte la façon dont vous écrivez et structurez votre code. Maintenant que vous possédez ces compétences, vous pouvez écrire du code Python plus précis et expressif, en utilisant pleinement les expressions et les instructions pour créer des programmes robustes et efficaces.