3.3. Bases et structure

Dans ce chapitre, nous allons étudier l’aspect structurel général du Python, à savoir comment on présente le code, quels sont les mots et les symboles importants... Le but est de donner une idée de la manière dont Python est construit et dont les programmes sont analysés. Ne soyez pas surpris si nous n’avons pas encore vu le sens de certaines expressions, elles ne sont là que pour donner un aperçu visuel et pour permettre de lire un programme Python : l’essentiel est de savoir reconnaitre les divers symboles et l’organisation logique, le reste viendra avec l’habitude.

Note

Même si l’aspect technique de ce chapitre peut rebuter, il est important au moins d’y jeter un œil afin d’avoir une idée générale de ce que l’on peut faire ou pas faire (les deux sections sur les variables sont vitales). On pourra souhaiter relire ce chapitre après s’être habitué au langage à travers les chapitres suivants, afin de mieux saisir certains points.

On peut décomposer les tokens en deux types :

  • ceux responsables de la structure générale, que l’on peut assimiler à l’aspect visuel : indentation, nouvelle ligne...
  • les autres, qui sont plus proches du langage : variables, mots-clés, délimiteurs...

3.3.1. Tokens de structure

3.3.1.1. Lignes

Il faut distinguer les lignes physiques (c’est à dire les lignes affichées à l’écran) des lignes logiques (telles qu’elles sont reconnues par Python). Généralement, une ligne physique correspond à une ligne logique, et une instruction Python ne peut pas s’étendre sur plusieurs lignes. Toutefois, il est possible de créer une ligne logique s’étendant sur plusieurs lignes réelles de deux manières :

  • explicitement : il faut indiquer à l’interpréteur qu’une instruction s’étend sur plusieurs lignes en ajoutant un antislash \ à la fin de la ligne. La ligne qui suit peut être indentée de n’importe quelle manière. Il est quand même conseillé d’adopter une certaine logique pour facilier la lecture.

    if a > 1 and b > 5 \
           and c < 3:
       print(1)
    
  • implicitement : une expression entourée de parenthèses () (ou de crochets, [] ou encore d’accolades {}) peut s’étendre sur plusieurs lignes.

    if (a > 1 and b > 5
           and c < 3):
       print(1)
    
    days = ['lundi', 'mardi', 'mercredi', 'jeudi',
            'vendredi', 'samedi', 'dimanche']
    

Note

Il est souvent plus lisible d’utiliser des parenthèses qu’un antislash. Dans les deux cas, l’indentation de la deuxième ligne est arbitraire :

if (a > 1 and b > 5
and c < 3):
    print(1)

On préfèrera malgré tout le premier exemple qui est beaucoup plus lisible.

Plusieurs instructions peuvent être placées sur une même ligne à condition d’être séparées par un point-virgule ;, mais cela nuit grandement à la lisibilité : on réservera donc cette écriture au mode interactif :

>>> print(1); print(2)
1
2

Enfin, une ligne vierge ou contenant uniquement des espaces ou tabulations est ignorée par l’interpréteur. Elles peuvent donc être insérées selon le bon vouloir du programmeur afin d’aérer le code et de séparer les blocs logiques.

Note

print() est une fonction permettant d’afficher du texte. Son utilisation sera détaillée dans le chapitre Entrées et sorties.

Pour le lecteur curieux, on notera que le token permettant d’indiquer la fin d’une ligne logique est nommé NEWLINE.

3.3.1.2. Blocs et indentation

En Python, les blocs d’instructions sont délimités par l’indentation. Pour cette raison, il n’est pas possible d’indenter librement les lignes comme on peut le faire dans d’autres langages.

.. testcode::

a = 1 if a > 2:

b = a + 1 print(b)

print(a)

Les lignes 3 et 4 ne seront exécutées que si la condition est vraie (ici ce n’est pas le cas), tandis que la dernière, qui n’est pas indentée, n’est pas inclue dans la condition.

Voici un exemple de mauvaise indentation :

>>>     print(1)
  File "<stdin>", line 1
    print(1)
    ^
IndentationError: unexpected indent

Reprenant l’exemple précédent, mais avec une légère erreur d’indentation :

>>> if a > 2:
...     b = a + 1
...      print(b)
  File "<stdin>", line 3
    print(b)
    ^
IndentationError: unexpected indent

L’on rajoute facilement un espace en début de ligne, espace qui sera compté comme une indentation et causera donc une erreur. Il faut y prendre garde.

Les indentations et désindentations successives sont représentées par les tokens INDENT et DEDENT. Ainsi, ce sont les indentations relatives qui sont importantes, et il n’est pas nécessaire qu’elles soient consistantes dans tout le code :

>>> a = 1
>>> if a < 2:
...     print(1)
... else:
...         print(2)
...
1

Mais par pitié, ne faites jamais ça !

3.3.1.3. Commentaires

Un commentaire est introduit par le signe # et s’étend jusqu’à la fin de la ligne. Il peut être ajouté à la fin d’une expression

>>> # commentaire
>>> # print(1)
>>> print(1) # un autre commentaire
1

Les commentaires sont ignorés au moment de l’analyse du code et ne sont donc pas considérés comme des tokens.

3.3.2. Autres tokens

3.3.2.1. Variables

Une variable permet de stocker une donnée qu’il sera possible de réutiliser plus tard. L’aspect dynamique des variables permet d’expliquer les interactions possibles avec un programme [1].

Le nom d’une variable :

  • doit contenir uniquement des caractères alphanumériques (lettres non-ASCII comprises), ou des underscores ;
  • doit commencer obligatoirement par un caractère alphabétique ou un underscore ;
  • est sensible à la casse (cela signifie que var est différent de Var ou encore VAR) ;
  • ne peut être nommée de la même manière qu’un mot-clé (voir section Mots-clés).

Note

La possibilité d’utiliser des caractères non-ASCII est apparue en Python 3. Toutefois, malgré la possibilité d’écrire ainsi le programme dans n’importe quelle langue (français, grec, chinois…), il est vivement déconseillé de le faire : pour qu’un programme vive, il doit y avoir des échanges, et l’anglais est (hélas) la langue qui est généralement comprise par tous.

Voir aussi

PEP 3131

Warning

Même si cela est possible, il est fortement déconseillé de renommer une fonction built-in :

>>> print = 2
>>> print('test')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

Dans la second expression, Python essaie d’appeler le chiffre 2 avec l’argument 'test'. Or un nombre n’a pas d’attribut call (c’est à dire qu’il ne peut pas se comporter comme une fonction), d’où l’erreur. En réalité, la fonction n’a pas réellement été renommée, mais simplement masquée par une variable du même nom (ceci sera plus clair lorsque nous étudierons le concept d’espace de noms), et nous pouvons revenir à l’état d’origine en supprimant la variable :

>>> del print
>>> print('test')
test

Utiliser un identifiant invalide lèvera une exception SyntaxError.

>>> a@ = 1
Traceback (most recent call last):
  File "<stdin>", line 1
    a@ = 1
     ^
SyntaxError: invalid syntax
>>> 2abc = 1
Traceback (most recent call last):
  File "<stdin>", line 1
    2abc = 1
       ^
SyntaxError: invalid syntax

Note

Il existe certaines conventions de nommage pour les variables (ainsi que pour les classes, fonctions, etc). Voir la section Conventions et style. En première approche, il est conseillé d’utiliser uniquement des minuscules et des underscores, et d’essayer de faire des noms aussi courts que possible (mais qui conservent un sens).

Il existe certaines catégories d’identifiants spéciaux, reconnaissables à un schéma particulier :

  • _* est utilisé pour indiquer les attributs protégés. Ces derniers ne seront pas importés par from module import *, ni directement accessible par la notation pointée.
  • __* est utilisé pour indiquer les attributs privés.
  • __*__ est utilisé par certains noms définis par le système. Une application ne devrait pas avoir à utiliser des identifiants de cette forme.

Nous les reverrons en temps voulu.

3.3.2.2. Littéraux

Les littéraux sont des valeurs constantes de certains types prédéfinis :

  • les chaines de caractères et autres types similaires (encadrés par des guillemets) ;
  • les nombres (entiers, flottants et imaginaires).

Voici quelques exemples : 'a', 'abc', 1, 2.3...

3.3.2.3. Mots-clés

Les mots-clés introduisent une instruction et ils servent à indiquer une action spéciale ou à définir une variable très spéciale. Contrairement aux fonctions, leur argument n’est pas entouré de parenthèses.

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

L’utilisation de ces mots-clés sera détaillée au cours du livre.

On parle aussi de mots réservés, car on n’a pas le droit de les utiliser comme nom de variables :

>>> for = 2
  File "<stdin>", line 1
    for = 2
        ^
SyntaxError: invalid syntax

Note

Dans les versions 2.x, print et exec sont des mots-clés. Ne vous étonnez donc pas de les voir être utilisés sans parenthèses dans des programmes écrits dans une ancienne version.

3.3.2.4. Opérateurs

Un opérateur permet d’exécuter, comme son nom l’indique, une opération particulière. Les valeurs auxquelles ils sont appliqués sont appelées opérandes.

Il existe de nombreux opérateurs, listés dans le tableau Liste des opérateurs. Leurs effets sont différents selon les types avec lesquels ils sont utilisés, par exemple :

>>> 1 + 1
2
>>> 'a' + 'b'
'ab'

Leurs utilisations seront donc détaillées dans les sections associées à chaque type. Nous verrons aussi comment surcharger les opérateurs, c’est à dire les utiliser avec de nouveaux objets.

&       |       ^       ~
<<      >>      ==      !=
<       >       <=      >=
+       -       *       **
/       //      %

Il est nécessaire de faire attention à l’ordre des opérateurs, car ils ont des priorités différentes lors de l’évaluation [2].

Note

Il est de coutume de placer un espace autour de chaque opérateur ; comme toute convention, celle-ci n’est bonne à respecter que si elle ne nuit pas à la lisibilité : par exemple, on écrira plutôt (2+1)*4 que (2 + 1) * 4.

3.3.2.5. Délimiteurs

Voici la liste des délimiteurs :

(       )       [       ]       {       }
,       :       .       ;       @       =
+=      -=      *=      /=      //=     %=
&=      |=      ^=      >>=     <<=     **=

Notons que le point . sera aussi utilisé comme séparateur décimal.

3.3.3. Variables

Dans cette section nous allons voir plus en détails l’utilisation des variables.

3.3.3.1. Affectation

On parle aussi d’assignation. L’affectation se fait grâce au signe =. Elle permet d’attribuer une valeur à variable. Il est bien entendu possible de changer le contenu d’une variable en lui affectant une nouvelle valeur.

>>> a = 1
>>> a
1
>>> a = 'var'
>>> a
'var'

Lors de l’affectation, l’interpréteur effectue plusieurs actions :

  • il crée et stocke l’objet (et détermine son type) associé à la variable s’il n’existe pas encore ;
  • il stocke le nom de la variable ;
  • il effectue un lien (on parle de référence) entre l’objet et le nom de la variable ;
  • il attribue dynamiquement un type à l’objet.

Toutes les informations sont stockées dans la mémoire de l’ordinateur.

Warning

Il ne faut pas confondre l’opérateur d’affectation avec celui d’égalité (==).

3.3.3.2. Affectation calculée

Il est possible de raccourcir l’instruction a = a+1 [3] en a += 1. De même, cela est possible avec tous les autres opérateurs binaires.

>>> a = 0
>>> a += 2
>>> a
2
>>> a *= 8
>>> a
16
>>> a %= 3 + 2
>>> a
1

Au cas où la dernière affectation serait difficile à comprendre, il faut la lire ainsi: a = a % (3+2). Bien que l’on ait utilisé des chiffres dans cet exemple, l’affectation calculée est définie pour tous les opérateurs et reste valable quels que soient les types d’objets manipulés :

>>> s = 'ha'
>>> s *= 5
>>> s
'hahahahaha'

Note

On parle d’incrémentation dans le cas où l’on ajoute 1 par cette méthode, et de décrémentation dans le cas où l’on retranche 1.

Les notations du type a++ que l’on peut voir dans d’autres langages n’existent pas en Python.

3.3.3.3. Références

Lorsque l’on assigne le même objet à différentes variables, cet objet n’est pas créé à nouveau à chaque fois : une référence est créée. Elle peut être vue comme un lien entre les noms, et le contenu. Tant qu’au moins un lien existe, l’objet perdure. Il ne sera supprimé que lorsque la dernière référence disparait (grâce au ramasse-miettes).

Par exemple, le code suivant ne créera qu’un seul objet, mais trois références :

>>> x = 1
>>> y = 1
>>> z = 1

Il est d’ailleurs possible de vérifier que l’objet est bien unique grâce au mot-clé is (qui sera détaillé plus loin) :

>>> x is y
True

3.3.3.4. Affichage d’une variable

Nous prendrons un peu d’avance afin d’aborder le point essentiel qu’est l’affichage d’une variable dans le mode interactif ou dans le terminal pour un programme lancé depuis la ligne de commande. Nous avons déjà vu la première méthode, uniquement en mode interactif, qui consiste à écrire le nom de la variable au prompt :

>>> var = 'a'
>>> var
'a'

La seconde solution est d’utiliser la fonction print().

>>> print(var)
a
>>> print('chaine')
chaine

Elle admet une grande diversité d’arguments, que nous étudierons plus tard. Notons simplement que si plusieurs arguments sont fournis, alors ils seront tous affichés sur la même ligne, séparés par un espace :

>>> print('Nous sommes le', 2, 'décembre.')
Nous sommes le 2 décembre.

Note

Remarquez que dans le premier cas, 'a' est affiché, tandis que dans le second l’on obtient a. En fait, print affiche la valeur de l’objet, tandis que l’autre méthode affiche la représentation de l’objet. Nous verrons cela en détails plus loin.

3.3.3.5. Affectations multiples

Il est possible d’affecter plusieurs valeurs à plusieurs variables en une seule ligne :

>>> a, b, c = 1, 2, 'var'
>>> a
1
>>> b
2
>>> c
'var'

On peut aussi affecter la même valeur à plusieurs variables d’un seul coup de la manière suivante :

>>> a = b = 1
>>> a, b
(1, 1)

Toutefois cette écriture peut avoir des effets pervers, car Python crée deux références d’un même objet, et non deux objets différents : ainsi, si on modifie l’objet à travers une des deux variables, alors la modification sera répercutée dans la deuxième. Pour illustrer ce fait, créons deux listes qui contiennent à l’origine la séquence de chiffre 1, 2, 3, et nous cherchons ensuite à remplacer le dernier chiffre de la seconde liste uniquement :

>>> l1 = l2 = [1, 2, 3]
>>> l2[2] = 5
>>> l2
[1, 2, 5]
>>> l1
[1, 2, 5]

Ainsi que nous le voyons, la valeur de la première liste a aussi changé ! On peut vérifier que les deux listes ne sont pas deux objets distincts :

>>> l1 is l2
True

Il existe plusieurs solutions à ce problème, que nous verrons dans le chapitre sur les listes. Nous esquissons une première solution dans la note.

Note

Reprenons l’exemple précédent où l’on a assigné la valeur 1 à a et b, et changeons la valeur de b :

>>> a = b = 1
>>> b = 2
>>> a, b
(1, 2)

Pourquoi a n’a-t-il pas pris la valeur 2 lui aussi ? La raison est que nous avons affecté une nouvelle valeur à la variable b, et donc un nouvel objet, ce qui ne modifie en rien l’objet précédent. Ainsi, une première méthode pour contourner le problème précédent consiste à recréer une liste et à l’assigner à b.

3.3.3.6. Échange de valeurs

Parfois l’on souhaite échanger les valeurs de deux variables. Là où d’autres langages obligent le programmeur à utiliser une variable intermédiaire, Python offre une solution élégante :

>>> x, y = y, x

Ceci est une conséquence directe de l’affectation multiples vue juste avant. Ainsi, il est de même possible d’échanger les valeurs de plusieurs variables :

>>> c, a, b = a, b, c

Ici, a prend la valeur qu’avait b, qui prend la valeur de c, qui lui-même prend l’ancienne valeur de a. Toutes ces assignations se font d’un seul coup, et non une par une (sans quoi cela ne fonctionnerait pas). Vérifions-le sur un exemple particulier :

>>> x, y = 1, 2
>>> x, y = y, x
>>> print(x, y)
2 1

3.3.3.7. Suppression d’une variable

Pour supprimer une variable, nous utilisons le mot-clé del, qui accepte un ou plusieurs arguments. Dans l’exemple qui suit, nous supposons que les variables ont été déclarées précédemment.

>>> del a
>>> del b, c
>>> del a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

Nous remarquons qu’une exception est levée si nous essayons de supprimer une variable qui n’existe pas (ou plus).

3.3.4. Résumé

Dans ce chapitre nous avons expliqué la construction d’un programme Python, en détaillant les structures et les identifiants reconnus par le langage.

Les différents tokens non liés à la structure sont les suivants :

  • identifiants de variables ;
  • les littéraux ;
  • mots-clés ;
  • opérateurs ;
  • délimiteurs.

Les espaces permettent de séparer les tokens, mais ils n’en sont pas eux-mêmes.

Ensuite, nous avons détaillé l’utilisation des variables : création et gestion par l’ordinateur, suppression et affichage.

3.3.5. Exercices

  1. Essayer de changer le contenu de deux variables sans utiliser l’affectation multiple.

Footnotes

[1]Par exemple, une page internet en HTML pur ne saurait être dynamique car ce langage ne possède pas de variables.
[2]Au même titre que les opérateurs en mathématiques.
[3]En mathématiques, cette notation n’a bien entendu aucun sens. Toutefois, comme il est précisé, plus haut, il ne s’agit pas d’une égalité mais d’une affectation.