3.4. Types de base

Nous allons maintenant étudier les principaux objets utilisés en Python, que nous appellerons des types : il s’agit d’objets relativement simples, comparés à ceux que l’on peut trouver ou construire dans des modules spécialisés, et ces types représentent des objets assez intuitifs :

  • les nombres (entier, à virgule...) ;
  • les listes, les chaines de caractères et d’autres séquences ;
  • les booléens, l’élément nul...

Nous ne détaillerons pas toutes les subtilités ni les usages des types tout de suite, et nous nous contenterons de jeter un œil à leur définition, à leurs principales caractéristiques ainsi que leur comportement en fonction des opérateurs.

3.4.1. Introduction

Commençons par présenter deux fonctions que nous utiliserons continuellement :

type(object)

Retourne le type de object au moyen un objet de type.

isinstance(object, classinfo)

La variable classinfo peut être une classe ou un tuple de classes. La fonction retourne True si object est une instance d’une des classes de la liste ou d’une sous-classe.

Nous verrons quelques exemples explicites dans les sections qui suivent.

On préférera réserver l’usage de type() au mode interactif, quand on veut vérifier le type d’un objet : en effet, la fonction isinstance() est plus robuste car elle vérifie aussi si l’objet ne dérive pas d’une sous-classe des classes indiquées. De plus, comme elle retourne un booléen, la fonction est plus “propre” pour écrire un test. Comparer les écritures suivantes :

>>> type(1) == int
True
>>> isinstance(1, int) is True
True

On aurait même pu écrire omettre le is True comme la syntaxe est assez claire.

Note

type() retourne en fait l’objet de classe et permet donc de construire directement un objet du même type que son argument :

>>> type(1)('102')
102

qui a bien le même effet que :

>>> int('102')
102

Cette construction peut être utile si on veut convertir un objet dans le même type qu’un second, sans connaitre ce type à l’avance.

3.4.2. Nombres

3.4.2.1. Introduction

Cette section suppose que le lecteur possède quelques notions de mathématiques, même si certains aspects un peu plus avancés sont traités afin de viser l’exhaustivité.

Note

Tous les objets nombres dérivent de la classe numbers.Number.

3.4.2.2. Flottants

Les flottants, ou nombres réels, représentent les nombres à virgule. Ils peuvent être représentés de plusieurs manières :

  • notation à virgule (ex. : 1.1) : la partie entière est séparée de la partie décimale par un point [1] ;
  • notation scientifique (ex. : 11e-1) : un nombre est mis multiplié par la puissance de 10 indiqué après un e (minuscule ou majuscule) : mathématiquement, cela revient a écrire 11 \times 10^{-1} = 1.1.

La fonction float() permet de convertir un nombre en flottant.

La précision de ces nombres dépend entièrement de la machine sur laquelle est exécutée Python, même si cela n’est pas affiché explicitement à l’écran.

>>> 1.1
1.1
>>> type(1.1)
<class 'float'>
>>> float(1)
1.0
>>> type(1.0)
<class 'float'>
>>> 2e-2
0.02
>>> type(2e-2)
<class 'float'>

Un nombre entier dont la partie décimale est explicitement représentée est considéré comme un nombre à virgule. Cela pouvait être utile dans les versions précédentes mais beaucoup moins dans la version 3.

Nous remarquons un effet de bord indésirable lorsque l’on entre 1.1 : il s’agit d’un problème commun à tous les langages dès que l’on utilise des nombres flottants. Ceci est dû à la manière dont les nombres sont gérés.

Warning

Il faut faire très attention à utiliser le point et non la virgule pour les nombres décimaux : deux nombres séparés par une virgule seront considérés comme un tuple.

Warning

Les anciennes versions de Python peuvent afficher de manière incorrecte les nombres flottants à cause d’erreurs d’arrondis :

>>> 1.1 
1.1000000000000001

3.4.2.3. Entiers

Il s’agit des nombres entiers (“integer” en anglais, abrégé en “int”), positifs ou négatifs. Leur taille a pour seule limite la mémoire virtuelle du système (là où les nombres d’autres langages sont limités, par exemple les int en C ne peuvent dépasser 32 bits).

La fonction int() permet de convertir un objet en nombre entier. Si on lui transmet un nombre flottant, alors elle ne garde que la partie entière.

>>> 1
1
>>> type(1)
<class 'int'>
>>> 23923 * 985
23564155
>>> 1234567899876543212345678998765432123456789
1234567899876543212345678998765432123456789
>>> int(3.5)
3

Une manière de s’assurer qu’un nombre est bien entier est d’utiliser l’expression n == int(n), qui ne sera vraie que si n est un nombre entier à la base (même écrit sous forme de flottant) :

>>> a, b, c = 1, 1.0, 1.5
>>> a == int(a)
True
>>> b == int(b)
True
>>> c == int(c)
False

3.4.2.4. Complexes

Note

À moins que vous n’étudiez les sciences (et encore), cette section sera pour ainsi dire inutile et vous pouvez l’ignorer sans états d’âme.

Les nombres complexes (ou imaginaires) ont été inventés pour résoudre certains problèmes mathématiques. Depuis, ils sont utilisés dans de nombreux domaines (ingénierie, physique...). Un nombre complexe est de la forme a + bj [2], où a et b sont des réels [3] [4]. La forme utilisant un J est aussi acceptée.

On peut obtenir un nombre complexe grâce à la fonction complex().

>>> c = 2+3j
>>> c
(2+3j)
>>> type(c)
<class 'complex'>
>>> 2.2j
2.2j
>>> 1+2J
(1+2j)
>>> complex(3.5)
(3.5+0j)

Les parties réelles et imaginaires d’un nombre complexe sont accessibles avec les attributs real et imag (qui renvoient des float). Enfin, le conjugué s’obtient avec la méthode conjugate().

>>> c.real
2.0
>>> c.imag
3.0
>>> c.conjugate()
(2-3j)

Note

Il est impossible d’écrire simplement j car l’interpréteur le considère comme l’identifiant d’une variable. Le nombre purement imaginaire unité s’écrit donc 1j.

3.4.2.5. Booléens

Il s’agit d’une classe dérivée de int.

Les booléens indiquent une valeur de vérité : il n’existe ainsi que deux valeurs possibles : vrai (1, qui correspond à l’objet True) ou faux (0, soit False). La conversion en booléen se fait avec la fonction bool().

Nous reviendrons sur ces objets et leur utilisation dans le chapitre Structures de contrôle.

3.4.2.6. Opérations

La plupart des opérations numériques classiques (addition, soustraction, multiplication...) existent en Python et sont accessibles par les opérateurs idoines :

  • a + b : addition de a et b ;
  • a - b : soustraction de b à a ;
  • a * b : multiplication de a et b ;
  • a / b : division de a par b ;
  • a // b : division entière de a par b ;
  • a % b : reste de la division entière (modulo) de a par b ;
  • a**b : mise de a à la puissance b ;
  • -a : opposé de a (revient à multiplier par -1) [5] ;
  • +a : positif de a (revient à multiplier par 1).

Les comparaisons sont celles usuellement définies avec les nombres : un nombre est supérieur à un autre s’il est plus grand...

Ces opérations sont valables avec chacun des nombres définis précédemment et peuvent même être utilisées entre des nombres de types différents.

Voici quelques exemples d’utilisation :

>>> 2 + 3.5
5.5
>>> 3 * 6
18
>>> 3 - 7
-4
>>> 7 / 2
3.5
>>> 7 // 2
3
>>> 7 % 2
1
>>> (2+3j) + 0.1
(2.1+3j)
>>> -+-1
1

Le dernier exemple montre bien qu’il s’agit d’opérateur unaire (même si ce genre d’opérations est peu intéressant). L’opération modulo % est extrêmement utile pour déterminer si un nombre a est multiple d’un autre b : en effet, si a est un multiple de b, alors b divise exactement a et le reste est nul. Ainsi, l’expression a % b == 0 sera vraie :

>>> 9 % 3 == 0
True

car 9 est bien un multiple de 3.

Python sait reconnaitre un même nombre même s’il est admet plusieurs écritures différentes (donc même s’il est représenté par différents objets) :

>>> 1 == 1.0
True
>>> 1 == (1 + 0j)
True

Note

La coercition est une opération consistant à convertir de manière implicite le type d’un objet en un autre type lors d’une opération (grâce à l’usage implicite de la fonction built-in coerce et à la méthode spéciale de classe __coerce__), afin d’éviter au programmeur de devoir le faire manuellement. Ainsi, cela permet d’utiliser les opérations qui viennent d’être définies entre des nombres de types différents. Il s’agit d’une fonctionnalité dépréciée depuis Python 3, car elle contredisait l’idée du typage fort.

Ceci ne change pas réellement les habitudes du programmeur puisque, lors de la définition d’un nouveau type, il fallait et il faudra dans tous les cas prendre en charge explicitement les conversions.

3.4.3. Conteneurs et séquences

Les conteneurs sont des objets qui en contiennent d’autres. Une sous-catégorie très importante des conteneurs est celle des séquences : dans ces dernières, les objets contenus sont ordonnées selon un certain ordre. Dans la suite de cette section, nous allons étudier quelques séquences (listes, tuples et chaines de caractères), ainsi que les dictionnaires et les ensembles.

Dans une séquence, un objet est repéré par un indice (“index” en anglais), qui vaut 0 pour le premier élément de la séquence, et n-1 pour le dernier (n étant la longueur de celle-ci).

Warning

Il convient de faire très attention au fait que la numérotation des index commence à 0. Il s’agit d’une particularité en programmation, très commune.

Dans le cas d’un conteneur non ordonné, l’accès aux éléments — lorsqu’il est possible — se fait à l’aide d’une clé (“key”) : il s’agit d’un identifiant, qui peut être n’importe quel objet non modifiable (une chaine, un nombre, un tuple...).

Les index et clés sont indiqués entre crochets immédiatement après l’objet.

sequence[key]
sequence[index]

L’accès avancé aux éléments, notamment avec le slicing, sera abordé plus loin.

3.4.3.1. Listes

Une liste est délimitée par des crochets, et chaque élément est séparé du précédent par une virgule. Il s’agit d’un conteneur dont les différents éléments peuvent être modifiés grâce à une simple affectation. L’ajout se fait avec la méthode append(). Finalement, la fonction list() permet de convertir un objet en liste.

>>> l = ['a', 'b', 'c']
>>> l
['a', 'b', 'c']
>>> type(l)
<class 'list'>
>>> l[0]
'a'
>>> l[2]
'c'
>>> l[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> l[1] = 'e'
>>> l
['a', 'e', 'c']
>>> l.append('z')
>>> l
['a', 'e', 'c', 'z']

3.4.3.2. Tuples

Les tuples sont construits à partir de plusieurs éléments séparés par des virgules ; le tout peut éventuellement être délimité par des parenthèses. Les tuples sont très semblables aux listes, mis à part qu’ils sont immuables : comme conséquence, ils consomment moins de mémoire et ils sont à privilégier si l’on sait que l’on ne souhaite pas modifier la séquence d’objets.

La fonction tuple() permet de convertir en tuple.

>>> t1 = 'a', 'b', 'c', 'd'
>>> t1
('a', 'b', 'c', 'd')
>>> t = ('a', 'b', 'c', 'd')
>>> t[1]
'b'
>>> t[1] = 'e'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Puisque les parenthèses servent aussi à délimiter les expressions, un tuple constitué d’un seul élément doit se déterminer par une virgule.

>>> (1)
1
>>> type((1))
<class 'int'>
>>> (1,)
(1,)
>>> type((1,))
<class 'tuple'>

Warning

Cette particularité est régulièrement source d’erreurs, et l’on oublie trop aisément la virgule finale.

3.4.3.3. Chaines de caractères

Une chaine de caractères (“string”, abrégé en “str”), comme son nom l’indique, est un ensemble de lettres, que l’on utilisera pour des mots, des phrases, voire des textes entiers. Une chaine est représentée comme une séquence de caractères (chaine de longueur 1), même s’il n’existe aucune différence fondamentale entre ces deux concepts. La fonction de conversion est str().

Une chaine peut être délimitée par :

  • des apostrophes ' ;
  • des guillemets " ;
  • des triples apostrophes ''' ;
  • des triples guillemets """.

Les deux dernières notations permettent d’écrire une chaine sur plusieurs lignes.

Note

Contrairement à certains langages qui font la différence entre les apostrophes et les guillemets pour entourer, il n’y en a aucune en Python : les deux délimiteurs sont totalement équivalents.

Il s’agit d’un type immuable car il est impossible de changer individuellement l’un des caractères, mais il est possible d’accéder individuellement aux caractères par leurs indices.

>>> s = 'Hello'
>>> type(s)
<class 'str'>
>>> isinstance(s, str)
True
>>> s[1]
'e'
>>> s[1] = 'a'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> "string"
'string'
>>> print('''première ligne
...          deuxième ligne''')
première ligne
deuxième ligne

Warning

Il est impossible d’utiliser le délimiteur choisi dans la chaine elle-même : il est nécessaire de l’échapper à l’aide de l’antislash \.

>>> 'l'enfant'
  File "<stdin>", line 1
    'l'enfant'
            ^
SyntaxError: invalid syntax

Pour éviter cette erreur, on peut soit choisir d’entourer la chaine avec des guillemets, soit faire précéder l’apostrophe intérieur d’un antislash :

>>> "l'enfant"
"l'enfant"
>>> 'l\'enfant'
"l'enfant"

Warning

Rappelons que Python est un langage au typage fort : il ne considère donc pas '1' comme un nombre :

>>> '1' + 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't convert 'int' object to str implicitly

3.4.3.4. Dictionnaires

Un dictionnaire est un conteneur, délimités par des accolades, dont les éléments sont accessibles par une clé. Les paires clé-valeur s’écrivent clé: valeur et sont séparés par des virgules. Enfin, il est possible de changer la valeur associée à une clé ou d’ajouter une nouvelle paire par une simple affectation. La fonction intégrée dédiée à la création de dictionnaires est dict().

>>> dic = {'a': 'val', 3: 'x', 'key': 124}
>>> type(dic)
<class 'dict'>
>>> dic['a']
'val'
>>> dic[3]
'x'
>>> dic['key'] = 9
>>> dic
{'a': 'val', 3: 'x', 'key': 9}
>>> dic['new'] = 'Hello'
>>> dic
{'a': 'val', 'new': 'Hello', 3: 'x', 'key': 9}

Remarquons que la nouvelle valeur se retrouve en deuxième position : les dictionnaires ne sont pas des ensembles ordonnés, c’est à dire que l’on ne peut prédire à quel endroit s’ajoutera une paire. Ils ne sont pas adaptés pour stocker des objets qui doivent restés classés quoiqu’il arrive (pour cela il convient de se tourner vers les listes ou les tuples).

3.4.3.5. Ensembles

Les ensembles (“set”) Python sont très similaires aux ensembles mathématiques : il s’agit d’un ensemble d’objets non ordonnés et uniques. On peut les créer soit en utilisant la fonction set() (remarquons qu’il existe la fonction frozenset() pour créer un ensemble immuable), soit en utilisant les accolades {} et en plaçant à l’intérieur la liste des objets :

Il ne faut pas confondre les dictionnaires et les ensembles, bien que la syntaxe utilise le même délimiteur.

Voici la liste des opérateurs spécifiques aux ensembles (d’autres opérations communes à tous les conteneurs seront présentées bientôt) et de leurs effets :

  • a & b : intersection de a et b, soit l’ensemble des éléments communs aux deux ensembles ;
  • a | b : union de a et b, c’est à dire tous les éléments de chaque ensemble (en ne comptant qu’une fois ceux en communs) ;
  • a - b : sous-ensemble des éléments de a qui ne sont pas dans b (différence) ;
  • a ^ b : sous-ensembles des éléments de a et b qui ne sont communs à aucun des deux ensembles.

Ils ne trouvent que rarement une utilité, mais je vais ici donner un exemple, qui permettra de plus de mettre en applications les opérations. Imaginons que je souhaite collectionner les quinze cartes d’un certain jeu, (numérotées de 1 à 15) et que je ne m’intéresse pas à la quantité que je possède (un peu comme si on collait ces cartes dans un album). Dans ce cas les ensembles seront parfaitement adaptés pour représenter ma collection. Finalement je peux agrandir ma collection un achetant un blister de trois cartes :

>>> cards = set(range(1, 16))
>>> deck < cards
True
>>> cards - deck
{1, 2, 5, 6, 8, 10, 11, 13, 14, 15}
>>> blister = {2, 7, 11}
>>> deck | blister
{2, 3, 4, 7, 9, 11, 12}
>>> deck & blister
{7}
>>> blister - deck
{2, 11}

Nous avons d’abord vérifié que nos cartes était bien un sous-ensemble des de la collection, puis calculé successivement : les cartes qui nous manquent, les cartes que l’on a après avoir acheté le blister, les cartes en doublons puis les nouvelles cartes apportées par le blister. Les opérations peuvent bien sûr être enchainées :

>>> {1, 3} | {5} | {4, 10, 12}
{1, 3, 4, 5, 10, 12}

3.4.3.6. Autres conteneurs

Le module collections propose différents types de conteneurs qui peuvent être utilisés lorsque l’on a des besoins précis. Par exemple, afin de répondre à une question qui se pose souvent, on peut construire des dictionnaires ordonnées à partir de la classe OrderedDict du module standard . Toutefois, une réflexion plus ou moins poussée permet de trouver une méthode plus élégante qu’un dictionnaire ordonné (par exemple en choisissant une liste de tuple à la place...). On y trouvera aussi un objet pour fabriquer des compteurs (Counter).

Notons enfin l’existence des types bytes et bytearray, qui permettent de représenter des chaines de caractères ASCII.

La fonction range() retourne un objet de ce type, qui représente une suite de nombres entiers.

3.4.3.7. Opérations

De nombreuses opérations sont définies sur les conteneurs :

  • x in s : retourne vrai si l’objet x se trouve dans le conteneur s, sinon faux ;
  • x not in s : retourne vrai si l’objet x ne se trouve pas dans la conteneur s, sinon faux ;
  • s1 + s2 (séquences) : concaténation des séquences s1 et s2 ;
  • s * n, n * s (séquences) : création puis concaténation de n copies superficielles (“swallow”) de s (n doit être un entier positif).

Nous avons dit que les tuples étaient immuables, or nous présentons ici des opérations entre eux : en fait, crée un nouveau tuple, dans lequel il place les éléments des deux tuples d’origine.

Warning

Les copies réalisées avec la “multiplication” sont superficielles, c’est à dire que seules les références sont copiées :

>>> l = [[]]
>>> l *= 3
>>> l[0].append(3)
>>> l
[[3], [3], [3]]

De plus, il existe quelques fonctions qui peuvent être utiles.

len(s)

Retourne la longueur dans le conteneur s.

min(s)

Retourne le plus petit élément dans le conteneur s.

max(s)

Retourne le plus grand élément dans le conteneur s.

Note

Si s est un dictionnaire, alors toutes les opérations se font au niveau des clés.

Les séquences possèdent les deux méthodes suivantes :

  • s.index(i) : retourne l’indice de la première occurrence de l’objet i dans la séquence s ;
  • s.count(i) : compte le nombre d’occurrences de l’objet i dans la séquence s.

Mettons un peu en pratique toutes ces opérations :

>>> d = {'a': 1, 'b': 5}
>>> 'a' in d
True
>>> 5 in d
False
>>> max(d)
'b'
>>> t = (1, 3) + (5, 6, 1)
>>> t
(1, 3, 5, 6, 1)
>>> 8 not in t
True
>>> len(t)
5
>>> t.count(1)
2
>>> t.index(3)
1
>>> min(t)
1
>>> [1, 2] * 4
[1, 2, 1, 2, 1, 2, 1, 2]

3.4.3.8. Slicing

Le slicing permet d’extraire une sous-séquence d’une autre séquence (dans le cas où un seul élément est sélectionné, il est simplement retourné). La syntaxe générale est seq[i:j:k], où i, j et k sont des entiers, éventuellement négatifs : la sous-séquence sera composée de tous les éléments de l’indice i jusqu’à l’indice j-1, par pas de k (ce dernier nombre peut être omis).

Si i ou j sont négatifs, cela est équivalent à l’indice n-i et n-j, où n est la taille de la séquence (on a écrit explicitement le signe moins des variables i et j). Si k est négatif, alors l’ordre des éléments est inversé.

>>> l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> l[-3]
7
>>> l[7]
7
>>> l[2:5]
[2, 3, 4]
>>> l[-6:8]
[4, 5, 6, 7]
>>> l[-6:-2]
[4, 5, 6, 7]
>>> l[5:2:-1]
[5, 4, 3]
>>> l[2:9:3]
[2, 5, 8]

3.4.4. Autres objets

3.4.4.1. L’objet nul

L’objet nul est None. Il correspond à la valeur NULL (PHP, C...), ou encore nil (Pascal) d’autres langages.

Dans la plupart des cas, il signifie l’absence de valeur. Il est par exemple utilisé comme valeur de retour par les fonctions qui ne retournent aucune valeur explicitement.

3.4.4.2. Type (classe)

Toutes les classes (prédéfinies ou non) sont du type type :

>>> type(str)
<class 'type'>

Les méthodes et attributs d’une classe peuvent être explorées à l’aide de l’attribut __dict__ (toutefois cet attribut n’est pas fait pour être modifié, il vaut mieux utiliser les méthodes et attributs prévus à cet effet). Un chapitre ultérieur est dédié à la création et à l’utilisation des classes.

Note

type(), déjà utilisée de nombreuses fois, renvoie le constructeur de l’objet passé en argument. Ainsi, puisque le constructeur des types est justement la fonction type(), nous pouvons vérifier que l’objet retourné est la fonction type() elle-même. De même, le constructeur d’un nombre est la fonction int(), à laquelle on peut donner un argument qui est converti en int :

>>> type(int) is typetype(int) is type
True
>>> type(1)(3.4)
3

3.4.4.3. Module

Les modules sont des objets contenant un ensemble d’autres objets définis dans un fichier. Ces objets sont créés à l’aide du mot clé import :

>>> import os
>>> type(os)
<class 'module'>

Là aussi nous pouvons accéder à l’ensemble des objets à l’aide de l’attribut __dict__, mais nous utiliserons toujours la notation pointée pour obtenir un objet :

>>> sys.platform
'linux2'

Le module sys contient des informations utiles concernant le système d’exploitation et la configuration de Python. L’attribut platform indique quel est le système d’exploitation (nous aurions eu win32 avec Windows).

Nous attendrons là aussi le chapitre dédié à ce sujet pour en parler plus en détails.

3.4.5. Résumé

Dans ce chapitre, nous avons étudié de manière superficielle les principaux types du langage Python, ainsi que leurs opérations :

  • les nombres : entiers, à virgule, complexes et booléens ;
  • les conteneurs et les séquences : listes, tuples, chaines de caractères, dictionnaires ;
  • les objets nul et type.

Les fonctions isinstance() et type() permettant d’étudier le type d’un objet ont aussi été abordées.

3.4.6. Exercices

  1. Essayez les différentes opérations sur les divers types de nombres. Observez les résultats lorsque les types étaient différents à la base.
  2. De même, créez diverses listes et entrainez-vous à les manipuler.
  1. Choisir un nombre (par exemple 1) et divisez-le mille fois par 10. Multipliez-le ensuite mille fois par 10 (on pourra attendre d’avoir lu le chapitre sur les boucles). Expliquez le résultat.

  2. Créer un système de permissions qui fonctionne comme suit :

    • chaque permission est associée à un nombre (e.g. 1 : “peut écrire un article”, 2 : “peut éditer un article”...) ;
    • chaque groupe se voit attribué plusieurs permissions ;
    • un utilisateur peut appartenir à plusieurs groupes ;
    • tester si un utilisateur a certaines permissions (on pourra attendre la lecture du prochain chapitre pour répondre à cette question, voire celle sur les fonctions afin de simplifier le code).

Footnotes

[1]La notation des nombres à virgule diffère ici de celle habituellement adoptée en France.
[2]On a j² = -1.
[3]a est appelé partie réelle et b est appelé partie imaginaire.
[4]Les mathématiciens préfèrent utiliser la lettre i.
[5]Ainsi, les nombres négatifs ne sont pas des nombres en eux-mêmes, mais plutôt considéré comme les opposés des nombres positifs.