Types de base et expressions#

Hide code cell source

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
import seaborn as sns

sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)

Les entiers (int)#

Python dispose d’un type entier remarquable par une propriété fondamentale : les entiers Python sont de précision arbitraire. Contrairement aux langages comme C ou Java où les entiers sont limités à 32 ou 64 bits, Python peut représenter et manipuler des entiers aussi grands que la mémoire disponible le permet. Cela le rend particulièrement adapté à la cryptographie, à la théorie des nombres et à tout calcul nécessitant une grande précision.

# Les entiers Python n'ont pas de limite de taille
grand_nombre = 2 ** 1000
print(grand_nombre)
print(f"Nombre de chiffres : {len(str(grand_nombre))}")
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376
Nombre de chiffres : 302

Représentations dans différentes bases#

Python permet de déclarer des entiers dans différentes bases numériques à l’aide de préfixes dédiés :

# Binaire (base 2) avec le préfixe 0b
entier_binaire = 0b1010_1100
print(f"Binaire 0b10101100 = {entier_binaire} en décimal")

# Octal (base 8) avec le préfixe 0o
entier_octal = 0o755
print(f"Octal 0o755 = {entier_octal} en décimal")

# Hexadécimal (base 16) avec le préfixe 0x
entier_hex = 0xFF_A0
print(f"Hexadécimal 0xFFA0 = {entier_hex} en décimal")

# Conversion vers différentes bases
n = 255
print(f"{n} en binaire : {bin(n)}")
print(f"{n} en octal : {oct(n)}")
print(f"{n} en hexadécimal : {hex(n)}")
Binaire 0b10101100 = 172 en décimal
Octal 0o755 = 493 en décimal
Hexadécimal 0xFFA0 = 65440 en décimal
255 en binaire : 0b11111111
255 en octal : 0o377
255 en hexadécimal : 0xff

Remarque 2

Python autorise l’utilisation de tirets bas _ comme séparateur de groupes dans les littéraux numériques, à la manière de l’espace en mathématiques : 1_000_000 est équivalent à 1000000. Cette convention améliore la lisibilité des grands nombres sans aucun impact sur leur valeur.

Opérations arithmétiques#

a, b = 17, 5

print(f"{a} + {b} = {a + b}")       # Addition
print(f"{a} - {b} = {a - b}")       # Soustraction
print(f"{a} * {b} = {a * b}")       # Multiplication
print(f"{a} / {b} = {a / b}")       # Division (toujours un float)
print(f"{a} // {b} = {a // b}")     # Division entière (quotient)
print(f"{a} % {b} = {a % b}")       # Modulo (reste)
print(f"{a} ** {b} = {a ** b}")     # Puissance
17 + 5 = 22
17 - 5 = 12
17 * 5 = 85
17 / 5 = 3.4
17 // 5 = 3
17 % 5 = 2
17 ** 5 = 1419857

Définition 1 (Division entière et modulo)

L’opérateur // effectue la division entière : il retourne le quotient de la division euclidienne, arrondi vers le bas (floor division). L’opérateur % retourne le reste de cette division. Ces deux opérateurs vérifient toujours la relation a == (a // b) * b + (a % b). Attention : // arrondit vers le bas (et non vers zéro), ce qui affecte le comportement avec les nombres négatifs : -7 // 2 vaut -4, pas -3.

# Comportement avec les négatifs
print(-7 // 2)   # -4 (arrondi vers le bas)
print(-7 % 2)    # 1  (reste toujours positif avec un diviseur positif)
-4
1

Les flottants (float)#

Les flottants Python suivent la norme IEEE 754 en double précision (64 bits), qui est le standard universel pour la représentation des nombres réels en informatique. Cette norme utilise 1 bit de signe, 11 bits d’exposant et 52 bits de mantisse, ce qui donne environ 15 à 17 chiffres significatifs décimaux.

# Déclaration de flottants
x = 3.14159
y = 2.0
z = 1.5e-3    # Notation scientifique : 0.0015
w = 1_234.567

print(type(x), x)
print(type(z), z)
<class 'float'> 3.14159
<class 'float'> 0.0015

Précision limitée et erreurs d’arrondi#

La représentation en base 2 de certaines fractions décimales est infinie, exactement comme 1/3 est infini en base 10. Cela provoque des erreurs d’arrondi inévitables :

# Le fameux problème de précision flottante
print(0.1 + 0.2)
print(0.1 + 0.2 == 0.3)  # False !

# Solution : utiliser math.isclose pour les comparaisons
import math
print(math.isclose(0.1 + 0.2, 0.3))
print(math.isclose(0.1 + 0.2, 0.3, rel_tol=1e-9))
0.30000000000000004
False
True
True

Remarque 3

Ne jamais comparer des flottants avec ==. Utiliser systématiquement math.isclose(a, b, rel_tol=1e-9) (tolérance relative) ou math.isclose(a, b, abs_tol=1e-9) (tolérance absolue) selon le contexte. La tolérance relative est appropriée pour des nombres de grandeur similaire ; la tolérance absolue est préférable quand l’un des nombres peut être proche de zéro.

# round() pour l'affichage (ne résout pas le problème de fond)
print(round(0.1 + 0.2, 10))
print(round(2.675, 2))  # Surprise : vaut 2.67, pas 2.68

# Valeurs spéciales IEEE 754
import math
print(float('inf'))       # Infini positif
print(float('-inf'))      # Infini négatif
print(float('nan'))       # Not a Number
print(math.isinf(1/0))   # False car ZeroDivisionError pour les entiers
print(math.isnan(float('nan')))
0.3
2.67
inf
-inf
nan
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Cell In[8], line 10
      8 print(float('-inf'))      # Infini négatif
      9 print(float('nan'))       # Not a Number
---> 10 print(math.isinf(1/0))   # False car ZeroDivisionError pour les entiers
     11 print(math.isnan(float('nan')))

ZeroDivisionError: division by zero

Le module decimal pour la précision exacte#

Quand la précision exacte est indispensable — calculs financiers, comptabilité — le module decimal de la bibliothèque standard offre une arithmétique décimale à précision arbitraire :

from decimal import Decimal, getcontext

# Précision par défaut : 28 chiffres significatifs
print(Decimal('0.1') + Decimal('0.2'))
print(Decimal('0.1') + Decimal('0.2') == Decimal('0.3'))

# Augmenter la précision
getcontext().prec = 50
print(Decimal(1) / Decimal(3))

Les booléens (bool)#

Le type bool représente les valeurs de vérité : True et False. En Python, bool est une sous-classe de int : True vaut 1 et False vaut 0, ce qui permet des usages arithmétiques parfois surprenants mais cohérents.

print(type(True))
print(isinstance(True, int))   # True est un int !
print(True + True)             # 2
print(True * 5)                # 5
print(False + 1)               # 1

Opérateurs logiques#

# Opérateurs and, or, not
print(True and False)
print(True or False)
print(not True)

# Comparaisons qui retournent des booléens
x = 10
print(x > 5)
print(x == 10)
print(x != 7)
print(3 <= x <= 15)  # Chaînage de comparaisons (pythonique)

Valeurs falsy et truthy#

Définition 2 (Valeurs falsy et truthy)

En Python, tout objet possède une valeur de vérité. Un objet est dit falsy s’il est évalué à False dans un contexte booléen. Les valeurs falsy sont : False, None, 0, 0.0, 0j, la chaîne vide "", la liste vide [], le tuple vide (), le dictionnaire vide {}, l’ensemble vide set(), et tout objet dont la méthode __bool__ retourne False ou dont la méthode __len__ retourne 0. Tout le reste est truthy.

# Exemples de valeurs falsy
valeurs_falsy = [False, None, 0, 0.0, "", [], {}, set()]
for v in valeurs_falsy:
    print(f"bool({v!r}) = {bool(v)}")

Court-circuit (short-circuit evaluation)#

Les opérateurs and et or utilisent l’évaluation en court-circuit : ils n’évaluent le second opérande que si nécessaire. De plus, ils retournent l’un de leurs opérandes, pas nécessairement un booléen :

# and retourne le premier falsy trouvé, ou le dernier opérande
print(1 and 2)         # 2
print(0 and 2)         # 0
print([] and "hello")  # []

# or retourne le premier truthy trouvé, ou le dernier opérande
print(1 or 2)          # 1
print(0 or 2)          # 2
print([] or "défaut")  # "défaut"

# Usage courant : valeur par défaut
nom = None
affichage = nom or "Anonyme"
print(affichage)

Les chaînes de caractères (str)#

Les chaînes Python sont des séquences immuables de caractères Unicode. Python 3 utilise Unicode (UTF-8) par défaut, ce qui signifie que les chaînes peuvent contenir n’importe quel caractère : lettres accentuées, idéogrammes chinois, émojis, symboles mathématiques.

Littéraux et formes spéciales#

# Différentes façons de déclarer une chaîne
s1 = 'guillemets simples'
s2 = "guillemets doubles"
s3 = """chaîne
sur plusieurs
lignes"""

# Chaîne brute (raw string) : les \ ne sont pas interprétés
chemin = r"C:\Users\alice\Documents"
print(chemin)

# Chaîne d'octets (bytes)
octets = b"données binaires"
print(type(octets))

# Chaîne f (f-string) — le format moderne
prenom = "Alice"
age = 30
print(f"Je m'appelle {prenom} et j'ai {age} ans.")
print(f"Le carré de {age} est {age**2}.")
print(f"Pi ≈ {3.14159:.4f}")   # Formatage avec spécificateur

Méthodes essentielles#

texte = "  Bonjour, monde !  "

# Nettoyage
print(repr(texte.strip()))        # Supprime les espaces aux extrémités
print(repr(texte.lstrip()))       # Supprime à gauche seulement
print(repr(texte.rstrip()))       # Supprime à droite seulement

# Transformation de casse
s = "python est génial"
print(s.upper())
print(s.capitalize())
print(s.title())

# Recherche et remplacement
print(s.replace("python", "Python"))
print(s.find("est"))              # Indice de la première occurrence
print(s.count("e"))               # Nombre d'occurrences
print("génial" in s)              # Test d'appartenance
# split et join
phrase = "un,deux,trois,quatre"
mots = phrase.split(",")
print(mots)

recombine = " - ".join(mots)
print(recombine)

# startswith et endswith
url = "https://www.example.com"
print(url.startswith("https"))
print(url.endswith(".com"))

Slicing (découpage)#

s = "abcdefghij"

print(s[0])       # Premier caractère
print(s[-1])      # Dernier caractère
print(s[2:5])     # Du 3ème au 5ème (exclu)
print(s[:4])      # Les 4 premiers
print(s[6:])      # Du 7ème à la fin
print(s[::2])     # Un sur deux
print(s[::-1])    # Inversé

Remarque 4

L’immuabilité des chaînes signifie qu’on ne peut pas modifier un caractère individuel (s[0] = 'A' lève une TypeError). Pour construire une chaîne modifiée, on utilise les méthodes qui retournent de nouvelles chaînes, ou on passe par une liste de caractères que l’on rejoint ensuite avec ''.join(). Cette immuabilité est une garantie de sécurité : une chaîne passée à une fonction ne peut jamais être altérée par celle-ci.

None#

None est l’unique instance du type NoneType. Il représente l”absence de valeur — l’équivalent Python de null dans d’autres langages. C’est la valeur de retour implicite des fonctions qui ne retournent rien explicitement.

# None est une valeur sentinelle
def fonction_sans_retour():
    x = 42  # calcule quelque chose mais ne retourne rien

resultat = fonction_sans_retour()
print(resultat)        # None
print(type(resultat))  # <class 'NoneType'>

# None est falsy
print(bool(None))      # False

Définition 3 (Comparaison avec None)

Pour tester si une variable vaut None, on utilise toujours is None (ou is not None), jamais == None. L’opérateur is teste l”identité d’objet (même adresse mémoire), tandis que == teste l”égalité de valeur. Comme None est un singleton — il n’existe qu’un seul objet None en mémoire — is None est à la fois sémantiquement correct et plus performant. Un objet personnalisé pourrait redéfinir __eq__ pour être égal à None, ce qui rendrait == None non fiable.

valeur = None
if valeur is None:
    print("Aucune valeur définie")

# Usage classique : paramètre optionnel avec None comme sentinelle
def saluer(nom=None):
    if nom is None:
        nom = "inconnu"
    return f"Bonjour, {nom} !"

print(saluer())
print(saluer("Alice"))

Typage dynamique et inférence#

Python est un langage à typage dynamique : le type d’une variable n’est pas déclaré à l’avance, il est déterminé au moment de l’exécution en fonction de la valeur qui lui est affectée. Une même variable peut changer de type au fil de l’exécution.

x = 42
print(type(x))   # int

x = "bonjour"
print(type(x))   # str

x = [1, 2, 3]
print(type(x))   # list

isinstance() et type()#

n = 42
print(type(n) == int)          # True, mais déconseillé
print(isinstance(n, int))      # True, à préférer
print(isinstance(True, int))   # True : bool est sous-classe de int
print(isinstance(n, (int, float)))  # Test sur plusieurs types

Remarque 5

isinstance() est généralement préférable à type() == car il tient compte de l’héritage : isinstance(True, int) retourne True, ce qui est sémantiquement correct. En revanche, type(True) == int retourne False, ce qui est souvent surprenant. Utiliser type() est justifié uniquement quand on veut tester le type exact, sans tenir compte des sous-classes.

Duck typing#

Python favorise le duck typing : « If it walks like a duck and quacks like a duck, then it’s a duck. » On ne vérifie pas le type d’un objet, mais la présence des méthodes ou attributs dont on a besoin. Cela rend le code plus flexible et générique.

# Cette fonction accepte n'importe quel objet itérable
def somme_elements(collection):
    total = 0
    for element in collection:
        total += element
    return total

print(somme_elements([1, 2, 3, 4]))        # Liste
print(somme_elements((10, 20, 30)))        # Tuple
print(somme_elements({100, 200, 300}))     # Ensemble

Conversions#

Python distingue les conversions implicites (effectuées automatiquement) des conversions explicites (demandées par le programmeur).

Conversions implicites#

Python n’effectue que très peu de conversions implicites, et uniquement dans des cas bien définis :

# int + float -> float (promotion automatique)
print(type(3 + 1.5))    # float
print(3 + 1.5)          # 4.5

# bool + int -> int
print(True + 5)         # 6

Conversions explicites#

# int() : convertit en entier
print(int(3.9))        # 3 (tronque, ne arrondit pas)
print(int("42"))       # 42
print(int("0xFF", 16)) # 255
print(int("0b1010", 2)) # 10
print(int(True))       # 1

# float() : convertit en flottant
print(float("3.14"))   # 3.14
print(float(42))       # 42.0
print(float("inf"))    # inf

# str() : convertit en chaîne
print(str(42))         # "42"
print(str(3.14))       # "3.14"
print(str(True))       # "True"
print(str(None))       # "None"

# bool() : convertit en booléen
print(bool(0))         # False
print(bool(1))         # True
print(bool(""))        # False
print(bool("hello"))   # True

Remarque 6

int() tronque les flottants vers zéro : int(3.9) vaut 3 et int(-3.9) vaut -3. Pour arrondir à l’entier le plus proche, on utilise round(). Pour les conversions invalides (par exemple int("bonjour")), Python lève une exception ValueError explicite plutôt que de retourner silencieusement une valeur par défaut — conformément à la philosophie « Errors should never pass silently ».

Visualisation de la hiérarchie des types numériques#

Hide code cell source

fig, ax = plt.subplots(figsize=(10, 7))
ax.set_xlim(0, 10)
ax.set_ylim(0, 9)
ax.axis('off')
ax.set_title("Hiérarchie des types numériques Python", fontsize=15,
             fontweight='bold', pad=20)

palette = sns.color_palette("muted", 4)

noeuds = [
    (5.0, 7.2, "bool\n(True / False)", palette[3], 1.4, 0.7),
    (5.0, 5.2, "int\n(précision arbitraire)", palette[0], 1.8, 0.7),
    (5.0, 3.2, "float\n(IEEE 754, 64 bits)", palette[1], 1.8, 0.7),
    (5.0, 1.2, "complex\n(partie réelle + imaginaire)", palette[2], 2.2, 0.7),
]

for (x, y, label, color, w, h) in noeuds:
    box = patches.FancyBboxPatch(
        (x - w/2, y - h/2), w, h,
        boxstyle="round,pad=0.15",
        linewidth=2,
        edgecolor=color,
        facecolor=color,
        alpha=0.25
    )
    ax.add_patch(box)
    border = patches.FancyBboxPatch(
        (x - w/2, y - h/2), w, h,
        boxstyle="round,pad=0.15",
        linewidth=2,
        edgecolor=color,
        facecolor='none'
    )
    ax.add_patch(border)
    ax.text(x, y, label, ha='center', va='center',
            fontsize=10, fontweight='bold', color=color)

# Flèches de conversion "est un sous-type de"
fleches = [
    (5.0, 6.85, 5.0, 5.55, "sous-classe de"),
    (5.0, 4.85, 5.0, 3.55, "converti vers"),
    (5.0, 2.85, 5.0, 1.55, "converti vers"),
]
for (x1, y1, x2, y2, label) in fleches:
    ax.annotate('', xy=(x2, y2), xytext=(x1, y1),
                arrowprops=dict(arrowstyle='->', color='#666666', lw=2))
    ax.text((x1 + x2) / 2 + 1.1, (y1 + y2) / 2, label,
            ha='center', va='center', fontsize=8, color='#888888',
            style='italic')

# Exemples de conversions
exemples = [
    (2.0, 7.2, "int(True) → 1\nbool(0) → False"),
    (2.0, 5.2, "float(42) → 42.0\nint(3.7) → 3"),
    (2.0, 3.2, "complex(1.5) → (1.5+0j)"),
]
for (x, y, texte) in exemples:
    ax.text(x, y, texte, ha='center', va='center',
            fontsize=7.5, color='#555555', style='italic',
            bbox=dict(boxstyle='round,pad=0.3', facecolor='#f8f8f8',
                      edgecolor='#cccccc', alpha=0.9))

ax.text(5.0, 8.5,
        "bool ⊂ int  ·  int → float → complex",
        ha='center', va='center', fontsize=10, color='#555555')

plt.tight_layout()
plt.show()

Résumé#

Dans ce chapitre, nous avons exploré les types de base de Python :

  • Les entiers (int) sont de précision arbitraire, supportent plusieurs bases (0b, 0o, 0x) et les opérateurs arithmétiques usuels incluant // (division entière), % (modulo) et ** (puissance).

  • Les flottants (float) suivent la norme IEEE 754 en double précision, avec les erreurs d’arrondi inhérentes. On compare toujours des flottants avec math.isclose(), jamais avec ==. Pour la précision exacte, le module decimal est la solution appropriée.

  • Les booléens (bool) sont une sous-classe de int ; True vaut 1 et False vaut 0. Les opérateurs and et or utilisent l’évaluation en court-circuit et retournent l’un de leurs opérandes. Toute valeur Python a une valeur de vérité (falsy ou truthy).

  • Les chaînes (str) sont des séquences immuables de caractères Unicode. Les f-strings sont le moyen moderne et recommandé de formater des chaînes. Le slicing offre un accès flexible aux sous-séquences.

  • None est un singleton représentant l’absence de valeur ; on le compare toujours avec is None, jamais avec == None.

  • Python est à typage dynamique et favorise le duck typing : on s’intéresse aux capacités d’un objet, pas à son type. isinstance() est préférable à type() == pour les vérifications de type.

  • Les conversions explicites (int(), float(), str(), bool()) permettent de transformer les valeurs d’un type à l’autre ; les conversions implicites sont limitées et bien définies.

Dans le chapitre suivant, nous aborderons les structures de contrôle : conditions, boucles et le filtrage structurel avec match/case.