4. Travailler avec les nombres
Au chapitre 3, vous avez appris à créer des variables et à travailler avec les types numériques de base de Python : les entiers et les nombres à virgule flottante. Il est maintenant temps de mettre ces nombres au travail. Dans ce chapitre, vous allez apprendre à effectuer des calculs, à combiner des opérations et à utiliser les outils intégrés de Python pour travailler avec des données numériques.
Les nombres sont fondamentaux en programmation. Que vous calculiez des totaux, traitiez des mesures, gériez des stocks ou analysiez des données, vous aurez besoin d’effectuer des opérations arithmétiques. Python rend les opérations numériques simples et intuitives, mais il y a des détails importants à comprendre — en particulier sur la manière dont fonctionnent les différents types de division, comment les opérateurs interagissent et comment se comportent les nombres à virgule flottante.
À la fin de ce chapitre, vous serez à l’aise pour effectuer des calculs, comprendre le comportement des opérateurs et utiliser les fonctions numériques de Python pour résoudre des problèmes concrets.
4.1) Arithmétique de base : addition, soustraction et multiplication
Commençons par les opérations arithmétiques les plus fondamentales. Python utilise des symboles familiers pour les mathématiques de base, et ces opérations fonctionnent exactement comme dans l’arithmétique de tous les jours.
4.1.1) Addition avec l’opérateur +
L’opérateur + additionne deux nombres. Il fonctionne à la fois avec les entiers et les nombres à virgule flottante :
# basic_addition.py
# Adding integers
total = 15 + 27
print(total) # Output: 42
# Adding floats
price = 19.99 + 5.50
print(price) # Output: 25.49
# You can add multiple numbers in one expression
sum_total = 10 + 20 + 30 + 40
print(sum_total) # Output: 100L’addition est simple, mais il y a un détail important : lorsque vous additionnez un entier et un float, Python convertit automatiquement le résultat en float pour préserver la précision décimale :
# mixed_addition.py
result = 10 + 3.5
print(result) # Output: 13.5
print(type(result)) # Output: <class 'float'>Cette conversion automatique a lieu parce qu’un float peut représenter à la fois des nombres entiers et des décimaux, alors qu’un entier ne peut pas représenter de décimaux. Python choisit le type qui ne perdra pas d’information.
4.1.2) Soustraction avec l’opérateur -
L’opérateur - soustrait le deuxième nombre du premier :
# basic_subtraction.py
# Subtracting integers
difference = 100 - 42
print(difference) # Output: 58
# Subtracting floats
remaining = 50.75 - 12.25
print(remaining) # Output: 38.5
# Subtraction can produce negative results
balance = 20 - 35
print(balance) # Output: -15Comme pour l’addition, la soustraction promeut aussi les entiers en floats lorsque vous mélangez les types :
# mixed_subtraction.py
result = 100 - 0.01
print(result) # Output: 99.99
print(type(result)) # Output: <class 'float'>4.1.3) Multiplication avec l’opérateur *
L’opérateur * multiplie deux nombres :
# basic_multiplication.py
# Multiplying integers
product = 6 * 7
print(product) # Output: 42
# Multiplying floats
area = 3.5 * 2.0
print(area) # Output: 7.0
# Multiplying by zero always gives zero
result = 1000 * 0
print(result) # Output: 0La multiplication suit les mêmes règles de conversion de type. Lorsque vous multipliez un entier par un float, le résultat est un float :
# mixed_multiplication.py
result = 5 * 2.5
print(result) # Output: 12.5
print(type(result)) # Output: <class 'float'>
# Even if the result is a whole number
result = 4 * 2.0
print(result) # Output: 8.0 (note the .0)
print(type(result)) # Output: <class 'float'>Remarquez dans le dernier exemple que même si 4 × 2.0 vaut 8, Python le représente sous la forme 8.0 parce que l’un des opérandes était un float. Le type du résultat dépend des types des entrées, pas du fait que le résultat mathématique soit ou non un nombre entier.
4.1.4) Exemples pratiques avec l’arithmétique de base
Voyons comment ces opérations fonctionnent ensemble dans des scénarios réalistes :
# shopping_cart.py
# Calculate a shopping cart total
item1_price = 12.99
item2_price = 8.50
item3_price = 15.00
subtotal = item1_price + item2_price + item3_price
print(f"Subtotal: ${subtotal}") # Output: Subtotal: $36.49
tax_rate = 0.08
tax = subtotal * tax_rate
print(f"Tax: ${tax}") # Output: Tax: $2.9192
total = subtotal + tax
print(f"Total: ${total}") # Output: Total: $39.4092# temperature_change.py
# Calculate temperature change
morning_temp = 45.5
afternoon_temp = 68.2
change = afternoon_temp - morning_temp
print(f"Temperature increased by {change} degrees")
# Output: Temperature increased by 22.7 degreesCes opérations de base constituent la fondation de calculs plus complexes. Comprendre comment elles fonctionnent — en particulier comment Python gère les conversions de type — vous aidera à éviter les surprises lorsque vos programmes effectuent des calculs.
4.2) Division, division entière et reste : /, // et %
La division en Python est plus subtile que l’addition, la soustraction ou la multiplication. Python fournit trois opérateurs liés à la division, chacun ayant un rôle distinct. Comprendre quand utiliser chacun d’eux est crucial pour écrire des programmes corrects.
4.2.1) Division classique avec /
L’opérateur / effectue une « vraie division » (true division) — il renvoie toujours un résultat à virgule flottante, même lorsque vous divisez deux entiers :
# true_division.py
# Dividing integers
result = 10 / 2
print(result) # Output: 5.0 (note: float, not 5)
print(type(result)) # Output: <class 'float'>
# Division that doesn't result in a whole number
result = 10 / 3
print(result) # Output: 3.3333333333333335
# Dividing floats
result = 15.5 / 2.5
print(result) # Output: 6.2L’idée clé ici est que / produit toujours un float, même lorsque les deux opérandes sont des entiers et que le résultat est mathématiquement un nombre entier. C’est voulu : Python souhaite que la division se comporte de façon cohérente et préserve la précision décimale.
Ce comportement est différent de celui de certains autres langages de programmation où la division de deux entiers produit un résultat entier. En Python 3, si vous voulez un résultat entier, vous devez utiliser la division entière (que nous verrons juste après) ou convertir explicitement le résultat.
4.2.2) Division entière (floor division) avec //
L’opérateur // effectue une « division entière » (floor division) — il divise puis arrondit à l’entier inférieur le plus proche. Lorsque les deux opérandes sont des entiers, le résultat est un entier. Lorsqu’au moins un opérande est un float, le résultat est un float (mais toujours arrondi vers le bas) :
# floor_division.py
# Floor division with integers
result = 10 // 3
print(result) # Output: 3 (not 3.333...)
print(type(result)) # Output: <class 'int'>
# Even division still gives an integer
result = 10 // 2
print(result) # Output: 5 (integer, not 5.0)
print(type(result)) # Output: <class 'int'>
# Floor division with floats gives a float
result = 10.0 // 3
print(result) # Output: 3.0
print(type(result)) # Output: <class 'float'>« Arrondir vers le bas » signifie aller vers moins l’infini, pas simplement supprimer la partie décimale. Cela a de l’importance pour les nombres négatifs :
# floor_division_negative.py
# Positive numbers: rounds down (toward negative infinity)
result = 7 // 2
print(result) # Output: 3
# Negative numbers: still rounds toward negative infinity
result = -7 // 2
print(result) # Output: -4 (not -3!)
# Why -4? Because -3.5 rounded down (toward negative infinity) is -4
# Think of the number line: ... -4, -3.5, -3, -2, -1, 0, 1, 2, 3, 3.5, 4 ...La division entière est utile lorsque vous devez répartir des éléments en groupes ou calculer combien d’unités complètes tiennent dans une quantité :
# floor_division_practical.py
# How many complete dozens in 50 eggs?
eggs = 50
eggs_per_dozen = 12
complete_dozens = eggs // eggs_per_dozen
print(f"Complete dozens: {complete_dozens}") # Output: Complete dozens: 4
# How many full hours in 150 minutes?
minutes = 150
full_hours = minutes // 60
print(f"Full hours: {full_hours}") # Output: Full hours: 24.2.3) Reste (modulo) avec %
L’opérateur % (appelé « modulo » ou « mod ») renvoie le reste après la division. Il répond à la question : « Après la division, que reste-t-il ? »
# modulo_basic.py
# 10 divided by 3 is 3 with remainder 1
result = 10 % 3
print(result) # Output: 1
# 10 divided by 2 is 5 with remainder 0 (even division)
result = 10 % 2
print(result) # Output: 0
# Works with floats too
result = 10.5 % 3
print(result) # Output: 1.5L’opérateur modulo est extrêmement utile pour plusieurs schémas courants en programmation :
Tester si un nombre est pair ou impair :
# even_odd_check.py
number = 17
if number % 2 == 0:
print(f"{number} is even")
else:
print(f"{number} is odd")
# Output: 17 is oddObtenir les éléments restants :
# leftover_items.py
eggs = 50
eggs_per_dozen = 12
leftover = eggs % eggs_per_dozen
print(f"Leftover eggs: {leftover}") # Output: Leftover eggs: 2Ramener des valeurs dans une plage (comme l’arithmétique d’une horloge) :
# clock_arithmetic.py
# What hour is it 25 hours from now? (on a 12-hour clock)
current_hour = 10
hours_later = 25
future_hour = (current_hour + hours_later) % 12
print(f"Hour: {future_hour}") # Output: Hour: 114.2.4) Relation entre //, % et /
La division entière et le modulo sont étroitement liés. Ensemble, ils vous donnent le résultat complet d’une division :
# division_relationship.py
dividend = 17
divisor = 5
quotient = dividend // divisor # How many times 5 goes into 17
remainder = dividend % divisor # What's left over
print(f"{dividend} ÷ {divisor} = {quotient} remainder {remainder}")
# Output: 17 ÷ 5 = 3 remainder 2
# You can verify: quotient * divisor + remainder should equal dividend
verification = quotient * divisor + remainder
print(f"Verification: {quotient} × {divisor} + {remainder} = {verification}")
# Output: Verification: 3 × 5 + 2 = 17Cette relation est toujours vraie : pour tous nombres a et b (où b n’est pas zéro), a == (a // b) * b + (a % b).
4.2.5) Choisir le bon opérateur de division
Voici un guide rapide pour choisir quel opérateur utiliser :
- Utilisez
/lorsque vous avez besoin du résultat mathématique exact avec décimales (le plus courant dans les calculs) - Utilisez
//lorsque vous devez compter combien de groupes complets tiennent (comme des douzaines, des heures, des pages) - Utilisez
%lorsque vous avez besoin du reste ou de la quantité restante (comme vérifier pair/impair, faire des tours de valeur, trouver les restes)
# choosing_operators.py
# Scenario: Distributing 47 candies among 6 children
candies = 47
children = 6
# How many candies per child? (use //)
per_child = candies // children
print(f"Each child gets {per_child} candies") # Output: Each child gets 7 candies
# How many candies are left over? (use %)
leftover = candies % children
print(f"Leftover candies: {leftover}") # Output: Leftover candies: 5
# What's the exact average? (use /)
average = candies / children
print(f"Average per child: {average}") # Output: Average per child: 7.8333333333333334.3) Opérateurs d’affectation augmentée
En programmation, vous aurez souvent besoin de mettre à jour une variable en effectuant une opération sur sa valeur actuelle. Par exemple, ajouter à un total cumulatif, soustraire d’un solde ou multiplier une valeur par un facteur. Python fournit un moyen concis de faire cela avec les opérateurs d’affectation augmentée.
4.3.1) Ce que sont les opérateurs d’affectation augmentée
Un opérateur d’affectation augmentée combine une opération arithmétique avec une affectation. Au lieu d’écrire :
# traditional_update.py
count = 10
count = count + 5 # Add 5 to count
print(count) # Output: 15Vous pouvez écrire :
# augmented_update.py
count = 10
count += 5 # Same as: count = count + 5
print(count) # Output: 15Les deux versions font exactement la même chose, mais la version augmentée est plus concise et exprime plus clairement l’intention : « augmenter count de 5 ».
4.3.2) Tous les opérateurs d’affectation augmentée
Python fournit des opérateurs d’affectation augmentée pour toutes les opérations arithmétiques :
# all_augmented_operators.py
# Addition
x = 10
x += 5 # x = x + 5
print(f"After += 5: {x}") # Output: After += 5: 15
# Subtraction
x = 10
x -= 3 # x = x - 3
print(f"After -= 3: {x}") # Output: After -= 3: 7
# Multiplication
x = 10
x *= 4 # x = x * 4
print(f"After *= 4: {x}") # Output: After *= 4: 40
# Division
x = 10
x /= 2 # x = x / 2
print(f"After /= 2: {x}") # Output: After /= 2: 5.0
# Floor division
x = 10
x //= 3 # x = x // 3
print(f"After //= 3: {x}") # Output: After //= 3: 3
# Modulo
x = 10
x %= 3 # x = x % 3
print(f"After %= 3: {x}") # Output: After %= 3: 1
# Exponentiation (we'll see more about ** in section 4.6)
x = 2
x **= 3 # x = x ** 3 (2 to the power of 3)
print(f"After **= 3: {x}") # Output: After **= 3: 84.3.3) Cas d’usage courants pour l’affectation augmentée
Les opérateurs d’affectation augmentée brillent dans plusieurs schémas de programmation courants :
Accumuler un total :
# accumulating_total.py
total = 0
total += 10 # Add first item
total += 25 # Add second item
total += 15 # Add third item
print(f"Total: {total}") # Output: Total: 50Compter des occurrences :
# counting.py
count = 0
# Imagine these happen as we process data
count += 1 # Found one
count += 1 # Found another
count += 1 # Found another
print(f"Count: {count}") # Output: Count: 3Mettre à jour un solde :
# balance_updates.py
balance = 100.00
balance -= 25.50 # Purchase
balance += 50.00 # Deposit
balance -= 10.00 # Purchase
print(f"Balance: ${balance}") # Output: Balance: $114.5Appliquer des opérations répétées :
# repeated_operations.py
value = 100
value *= 1.1 # Increase by 10%
value *= 1.1 # Increase by 10% again
value *= 1.1 # Increase by 10% again
print(f"Value after three 10% increases: {value}")
# Output: Value after three 10% increases: 133.100000000000024.3.4) Détails importants sur l’affectation augmentée
L’affectation augmentée crée un nouvel objet pour les types immuables :
Souvenez-vous du chapitre 3 : les nombres sont immuables — vous ne pouvez pas changer la valeur d’un nombre, seulement créer de nouveaux nombres. Quand vous écrivez x += 5, Python crée un nouvel objet nombre et l’assigne à x. L’ancien objet nombre est abandonné (nous explorerons ce concept plus en profondeur au chapitre 17 lorsque nous parlerons du modèle objet de Python) :
# augmented_with_immutables.py
x = 10
print(id(x)) # Output: (some memory address)
x += 5
print(id(x)) # Output: (different memory address)Pour l’instant, comprenez simplement que x += 5 est équivalent à x = x + 5 — c’est une écriture abrégée pratique, pas une opération fondamentalement différente.
Vous ne pouvez pas utiliser une affectation augmentée avant qu’une variable n’existe :
# augmented_requires_existing.py
# This will cause an error:
# count += 1 # NameError: name 'count' is not defined
# You must initialize the variable first:
count = 0
count += 1 # Now this works
print(count) # Output: 1Les conversions de type se produisent de la même manière :
# augmented_type_conversion.py
x = 10 # integer
x += 2.5 # Add a float
print(x) # Output: 12.5
print(type(x)) # Output: <class 'float'>Le résultat suit les mêmes règles de conversion de type que les opérateurs classiques : si vous mélangez entiers et floats, le résultat est un float.
4.3.5) Quand utiliser l’affectation augmentée
Utilisez les opérateurs d’affectation augmentée chaque fois que vous mettez à jour une variable sur la base de sa valeur actuelle. Ils rendent votre code :
- Plus concis :
x += 5est plus court quex = x + 5 - Plus lisible : l’intention est immédiatement claire
- Moins sujet aux erreurs : vous réduisez le risque de mal retaper le nom de la variable
Comparez ces deux versions :
# comparison.py
# Without augmented assignment
accumulated_distance_in_kilometers = 0
accumulated_distance_in_kilometers = accumulated_distance_in_kilometers + 10
accumulated_distance_in_kilometers = accumulated_distance_in_kilometers + 25
# With augmented assignment
accumulated_distance_in_kilometers = 0
accumulated_distance_in_kilometers += 10
accumulated_distance_in_kilometers += 25La version avec affectation augmentée est plus claire et réduit le risque de fautes de frappe. Les opérateurs d’affectation augmentée sont une petite fonctionnalité, mais ils sont utilisés en permanence dans du vrai code Python.
4.4) Priorité des opérateurs et parenthèses
Lorsque vous combinez plusieurs opérations dans une seule expression, Python a besoin de règles pour déterminer l’ordre d’évaluation. Faut‑il calculer 2 + 3 * 4 comme (2 + 3) * 4 = 20 ou comme 2 + (3 * 4) = 14 ? La réponse dépend de la priorité des opérateurs — les règles qui déterminent quelles opérations sont effectuées en premier.
4.4.1) Comprendre la priorité des opérateurs
Python suit les mêmes règles de priorité que celles apprises en mathématiques : la multiplication et la division sont effectuées avant l’addition et la soustraction. On se souvient souvent de cela avec l’acronyme PEMDAS (Parentheses, Exponents, Multiplication/Division, Addition/Subtraction), même si nous verrons les exposants en section 4.6.
# precedence_basic.py
# Multiplication happens before addition
result = 2 + 3 * 4
print(result) # Output: 14 (not 20)
# Python calculates: 2 + (3 * 4) = 2 + 12 = 14
# Division happens before subtraction
result = 10 - 8 / 2
print(result) # Output: 6.0 (not 1.0)
# Python calculates: 10 - (8 / 2) = 10 - 4.0 = 6.0Voici l’ordre de priorité pour les opérateurs que nous avons vus jusqu’à présent (du plus élevé au plus faible) :
- Parenthèses
()— priorité la plus élevée, toujours évaluées en premier - Exponentiation
**— (nous verrons cela en section 4.6) - Multiplication, division, division entière, modulo
*,/,//,%— même niveau, évalués de gauche à droite - Addition, soustraction
+,-— même niveau, évalués de gauche à droite
Voyons plus d’exemples de fonctionnement de la priorité :
# precedence_examples.py
# Multiplication before addition
result = 5 + 2 * 3
print(result) # Output: 11 (5 + 6)
# Division before subtraction
result = 20 - 10 / 2
print(result) # Output: 15.0 (20 - 5.0)
# Multiple operations at the same level: left to right
result = 10 - 3 + 2
print(result) # Output: 9 ((10 - 3) + 2)
result = 20 / 4 * 2
print(result) # Output: 10.0 ((20 / 4) * 2)4.4.2) Utiliser les parenthèses pour contrôler l’ordre
Les parenthèses outrepassent la priorité par défaut. Les opérations à l’intérieur des parenthèses sont toujours effectuées en premier, quels que soient les opérateurs impliqués :
# parentheses_override.py
# Without parentheses: multiplication first
result = 2 + 3 * 4
print(result) # Output: 14
# With parentheses: addition first
result = (2 + 3) * 4
print(result) # Output: 20
# Another example
result = 10 - 8 / 2
print(result) # Output: 6.0
result = (10 - 8) / 2
print(result) # Output: 1.0Vous pouvez imbriquer des parenthèses pour créer des expressions plus complexes. Python évalue de l’intérieur vers l’extérieur :
# nested_parentheses.py
result = ((2 + 3) * 4) - 1
print(result) # Output: 19
# Step 1: (2 + 3) = 5
# Step 2: (5 * 4) = 20
# Step 3: 20 - 1 = 19
result = 2 * (3 + (4 - 1))
print(result) # Output: 12
# Step 1: (4 - 1) = 3
# Step 2: (3 + 3) = 6
# Step 3: 2 * 6 = 124.4.3) Quand les opérateurs ont la même priorité
Lorsque plusieurs opérateurs de même priorité apparaissent dans une expression, Python les évalue de gauche à droite (on parle d’« associativité à gauche ») :
# left_to_right.py
# Addition and subtraction: left to right
result = 10 - 3 + 2 - 1
print(result) # Output: 8
# Evaluation: ((10 - 3) + 2) - 1 = (7 + 2) - 1 = 9 - 1 = 8
# Multiplication and division: left to right
result = 20 / 4 * 2
print(result) # Output: 10.0
# Evaluation: (20 / 4) * 2 = 5.0 * 2 = 10.0
# This matters! Different order gives different result:
result = 20 / (4 * 2)
print(result) # Output: 2.54.4.4) Exemples pratiques avec la priorité
Regardons des scénarios réalistes où comprendre la priorité est important :
# temperature_conversion.py
# Convert Fahrenheit to Celsius: C = (F - 32) * 5 / 9
fahrenheit = 98.6
# Correct: parentheses ensure subtraction happens first
celsius = (fahrenheit - 32) * 5 / 9
print(f"{fahrenheit}°F = {celsius}°C")
# Output: 98.6°F = 37.0°C
# Wrong: without parentheses, multiplication happens first
# celsius = fahrenheit - 32 * 5 / 9 # This would be wrong!
# This would calculate: fahrenheit - ((32 * 5) / 9)# calculate_average.py
# Calculate average of three numbers
num1 = 85
num2 = 92
num3 = 78
# Correct: parentheses ensure addition happens before division
average = (num1 + num2 + num3) / 3
print(f"Average: {average}") # Output: Average: 85.0
# Wrong: without parentheses, only num3 is divided by 3
# average = num1 + num2 + num3 / 3 # This would be wrong!
# This would calculate: 85 + 92 + (78 / 3) = 85 + 92 + 26.0 = 203.0# discount_calculation.py
# Calculate price after discount and tax
original_price = 100.00
discount_rate = 0.20
tax_rate = 0.08
# Calculate discount amount
discount = original_price * discount_rate
# Calculate price after discount
discounted_price = original_price - discount
# Calculate final price with tax
final_price = discounted_price * (1 + tax_rate)
print(f"Final price: ${final_price}") # Output: Final price: $86.4
# Or in one expression (using parentheses to be clear):
final_price = (original_price * (1 - discount_rate)) * (1 + tax_rate)
print(f"Final price: ${final_price}") # Output: Final price: $86.44.4.5) Bonnes pratiques pour la priorité des opérateurs
Utilisez des parenthèses pour la clarté, même quand ce n’est pas strictement nécessaire :
Parfois, ajouter des parenthèses rend votre intention plus claire, même si elles ne changent pas le résultat :
# clarity_with_parentheses.py
# These are equivalent, but the second is clearer:
result = 2 + 3 * 4
result = 2 + (3 * 4) # Clearer: shows you know multiplication happens first
# Complex expressions benefit from parentheses:
result = (subtotal * tax_rate) + (subtotal * tip_rate) # Clear intentDécoupez les expressions complexes en étapes :
Quand une expression devient trop complexe, il est souvent préférable de la découper en plusieurs lignes :
# breaking_into_steps.py
# Instead of this complex one-liner:
result = ((price * quantity) * (1 - discount)) * (1 + tax)
# Consider breaking it into steps:
subtotal = price * quantity
discounted = subtotal * (1 - discount)
final = discounted * (1 + tax)
result = finalLa version en plusieurs étapes est plus facile à lire, déboguer et vérifier. Ne sacrifiez pas la clarté pour la concision.
En cas de doute, utilisez des parenthèses :
Si vous n’êtes pas sûr de la priorité, ajoutez des parenthèses. L’interpréteur Python ne s’en plaindra pas, et votre futur vous (ou d’autres développeurs lisant votre code) vous en sera reconnaissant.
Comprendre la priorité des opérateurs vous aide à écrire des expressions correctes et à lire le code des autres. Principe clé : lorsque vous combinez des opérations, réfléchissez à l’ordre d’évaluation, et utilisez des parenthèses pour rendre votre intention explicite.
4.5) Mélanger entiers et floats dans les expressions
Vous avez déjà vu que Python gère automatiquement le mélange d’entiers et de floats dans les opérations simples. Explorons maintenant ce comportement de manière plus systématique et comprenons les règles qui gouvernent la conversion de type dans les expressions numériques.
4.5.1) La règle de promotion de type
Lorsqu’une opération arithmétique en Python implique à la fois un entier et un float, Python convertit automatiquement (ou « promeut ») l’entier en float avant d’effectuer l’opération. Le résultat est toujours un float :
# type_promotion.py
# Integer + Float = Float
result = 10 + 3.5
print(result) # Output: 13.5
print(type(result)) # Output: <class 'float'>
# Float + Integer = Float (order doesn't matter)
result = 3.5 + 10
print(result) # Output: 13.5
print(type(result)) # Output: <class 'float'>
# This applies to all arithmetic operators
result = 5 * 2.0
print(result) # Output: 10.0 (float, not int)
print(type(result)) # Output: <class 'float'>Pourquoi Python fait‑il cela ? Parce que les floats peuvent représenter à la fois des nombres entiers et des décimaux, alors que les entiers ne peuvent pas représenter de décimaux. La conversion en float préserve toute l’information et évite de perdre de la précision.
Voici une représentation visuelle de la façon dont Python décide du type de résultat :
4.5.2) Promotion de type dans les expressions complexes
Lorsqu’une expression contient plusieurs opérations avec des types mélangés, Python applique la règle de promotion à chaque étape :
# complex_mixed_types.py
# Multiple operations with mixed types
result = 10 + 3.5 * 2
print(result) # Output: 17.0
print(type(result)) # Output: <class 'float'>
# What happens step by step:
# 1. 3.5 * 2 → 3.5 * 2.0 (2 promoted to float) → 7.0 (float)
# 2. 10 + 7.0 → 10.0 + 7.0 (10 promoted to float) → 17.0 (float)
# Another example
result = 5 / 2 + 3
print(result) # Output: 5.5
print(type(result)) # Output: <class 'float'>
# Step by step:
# 1. 5 / 2 → 2.5 (division always produces float)
# 2. 2.5 + 3 → 2.5 + 3.0 (3 promoted to float) → 5.5 (float)Une fois qu’une opération dans une expression produit un float, toutes les opérations suivantes impliquant ce résultat produiront aussi des floats.
4.5.3) Cas particulier : la division classique produit toujours un float
Souvenez-vous de la section 4.2 : l’opérateur / produit toujours un float, même lorsqu’il divise deux entiers :
# division_always_float.py
# Even when the result is a whole number
result = 10 / 2
print(result) # Output: 5.0 (not 5)
print(type(result)) # Output: <class 'float'>
# This means any expression with / will have a float result
result = 10 / 2 + 3 # 5.0 + 3 → 5.0 + 3.0 → 8.0
print(result) # Output: 8.0
print(type(result)) # Output: <class 'float'>Si vous voulez un résultat entier à partir d’une division, utilisez plutôt la division entière // (mais souvenez‑vous qu’elle arrondit vers le bas) :
# floor_division_integer.py
# Floor division with integers produces an integer
result = 10 // 2
print(result) # Output: 5 (integer)
print(type(result)) # Output: <class 'int'>
# But floor division with any float produces a float
result = 10.0 // 2
print(result) # Output: 5.0 (float)
print(type(result)) # Output: <class 'float'>4.5.4) Implications pratiques du mélange de types
Comprendre la promotion de type vous aide à prévoir et contrôler les types de vos résultats :
# practical_type_mixing.py
# Calculating price per item
total_cost = 47.50
num_items = 5
price_per_item = total_cost / num_items # Float / int → float
print(f"Price per item: ${price_per_item}")
# Output: Price per item: $4.75
# Calculating average (will be float even if inputs are integers)
total_points = 450
num_tests = 5
average = total_points / num_tests # Int / int → float
print(f"Average: {average}") # Output: Average: 90.0
# If you need an integer result, convert explicitly
average_rounded = int(total_points / num_tests)
print(f"Average (as integer): {average_rounded}")
# Output: Average (as integer): 904.5.5) Quand les résultats entiers comptent
Parfois, vous avez spécifiquement besoin de résultats entiers pour compter, indexer ou d’autres opérations discrètes. Voici comment vous assurer d’obtenir des entiers :
# ensuring_integer_results.py
# Using floor division when you need integer results
items = 47
items_per_box = 12
# How many complete boxes?
complete_boxes = items // items_per_box # Integer result
print(f"Complete boxes: {complete_boxes}")
# Output: Complete boxes: 3
# If you use regular division, you get a float
boxes_float = items / items_per_box
print(f"Boxes (float): {boxes_float}")
# Output: Boxes (float): 3.9166666666666665
# Converting float to integer (truncates toward zero)
boxes_truncated = int(boxes_float)
print(f"Boxes (truncated): {boxes_truncated}")
# Output: Boxes (truncated): 3Notez la différence : // arrondit vers le bas (vers moins l’infini), tandis que int() tronque vers zéro. Pour les nombres positifs, ils donnent le même résultat, mais pour les nombres négatifs ils diffèrent :
# truncation_vs_floor.py
# For positive numbers: same result
print(7 // 2) # Output: 3
print(int(7/2)) # Output: 3
# For negative numbers: different results
print(-7 // 2) # Output: -4 (rounds down toward negative infinity)
print(int(-7/2)) # Output: -3 (truncates toward zero)4.5.6) Éviter les résultats float inattendus
Parfois, vous pouvez être surpris d’obtenir un float là où vous attendiez un entier. Cela se produit généralement à cause de la division ou du mélange de types :
# unexpected_floats.py
# Calculating average - result is always float because of division
count = 10
total = 100
average = total / count
print(average) # Output: 10.0 (float, even though it's a whole number)
# If you need an integer and you know it divides evenly:
average_int = total // count
print(average_int) # Output: 10 (integer)
# Or convert explicitly:
average_int = int(total / count)
print(average_int) # Output: 10 (integer)Point essentiel : les règles de promotion de type de Python sont conçues pour préserver la précision et éviter la perte de données. Quand vous mélangez entiers et floats, ou lorsque vous utilisez la division classique, attendez‑vous à des résultats float. Si vous avez besoin d’entiers, utilisez la division entière ou une conversion explicite, tout en étant conscient de la façon dont elles gèrent l’arrondi.
4.6) Fonctions numériques intégrées utiles : abs(), min(), max() et pow()
Python fournit plusieurs fonctions intégrées qui réalisent des opérations numériques courantes. Ces fonctions fonctionnent avec les entiers et les floats, et ce sont des outils essentiels pour la programmation de tous les jours. Explorons les plus utilisées.
4.6.1) Valeur absolue avec abs()
La fonction abs() renvoie la valeur absolue d’un nombre — sa distance à zéro, sans tenir compte du signe :
# absolute_value.py
# Absolute value of negative numbers
result = abs(-42)
print(result) # Output: 42
# Absolute value of positive numbers (unchanged)
result = abs(42)
print(result) # Output: 42
# Works with floats too
result = abs(-3.14)
print(result) # Output: 3.14
# Absolute value of zero is zero
result = abs(0)
print(result) # Output: 0La fonction de valeur absolue est utile chaque fois que vous vous intéressez à la grandeur mais pas à la direction :
# practical_abs.py
# Calculate temperature difference (magnitude only)
morning_temp = 45.5
evening_temp = 38.2
difference = abs(evening_temp - morning_temp)
print(f"Temperature changed by {difference} degrees")
# Output: Temperature changed by 7.3 degrees
# Calculate distance between two points (always positive)
position1 = 10
position2 = 25
distance = abs(position2 - position1)
print(f"Distance: {distance}") # Output: Distance: 15
# Works the same regardless of order
distance = abs(position1 - position2)
print(f"Distance: {distance}") # Output: Distance: 154.6.2) Trouver le minimum et le maximum avec min() et max()
La fonction min() renvoie le plus petit de ses arguments, et max() renvoie le plus grand :
# min_max_basic.py
# Find minimum of two numbers
smallest = min(10, 25)
print(smallest) # Output: 10
# Find maximum of two numbers
largest = max(10, 25)
print(largest) # Output: 25
# Works with more than two arguments
smallest = min(5, 12, 3, 18, 7)
print(smallest) # Output: 3
largest = max(5, 12, 3, 18, 7)
print(largest) # Output: 18
# Works with floats and mixed types
smallest = min(3.5, 2, 4.1, 1.9)
print(smallest) # Output: 1.9Ces fonctions sont très utiles pour trouver les extrêmes dans des données :
# practical_min_max.py
# Find the best and worst test scores
test1 = 85
test2 = 92
test3 = 78
test4 = 95
highest_score = max(test1, test2, test3, test4)
lowest_score = min(test1, test2, test3, test4)
print(f"Highest score: {highest_score}") # Output: Highest score: 95
print(f"Lowest score: {lowest_score}") # Output: Lowest score: 78
# Clamp a value within a range
value = 150
minimum = 0
maximum = 100
# Ensure value is not below minimum
value = max(value, minimum)
# Ensure value is not above maximum
value = min(value, maximum)
print(f"Clamped value: {value}") # Output: Clamped value: 100Le schéma de « clamp » (serrer une valeur dans une plage) avec max() et min() ensemble est courant quand vous devez contraindre une valeur à une plage spécifique :
# clamping_pattern.py
def clamp(value, min_value, max_value):
"""Constrain value to be within [min_value, max_value]"""
return max(min_value, min(value, max_value))
# Test the clamp function
print(clamp(150, 0, 100)) # Output: 100 (too high, clamped to max)
print(clamp(-10, 0, 100)) # Output: 0 (too low, clamped to min)
print(clamp(50, 0, 100)) # Output: 50 (within range, unchanged)4.6.3) Exponentiation avec pow()
La fonction pow() élève un nombre à une puissance. Python dispose aussi de l’opérateur ** pour l’exponentiation, qui est le plus couramment utilisé, mais pow() offre quelques fonctionnalités supplémentaires :
# exponentiation.py
# Using the ** operator (most common)
result = 2 ** 3 # 2 to the power of 3
print(result) # Output: 8
result = 5 ** 2 # 5 squared
print(result) # Output: 25
# Using pow() function (equivalent)
result = pow(2, 3)
print(result) # Output: 8
result = pow(5, 2)
print(result) # Output: 25
# Negative exponents give fractions
result = 2 ** -3 # 1 / (2^3) = 1/8
print(result) # Output: 0.125
# Fractional exponents give roots
result = 9 ** 0.5 # Square root of 9
print(result) # Output: 3.0
result = 8 ** (1/3) # Cube root of 8
print(result) # Output: 2.0La fonction pow() peut prendre un troisième argument optionnel pour l’exponentiation modulaire (utile en cryptographie et en théorie des nombres, mais au‑delà de la portée de l’arithmétique de base) :
# modular_exponentiation.py
# pow(base, exponent, modulus) computes (base ** exponent) % modulus efficiently
result = pow(2, 10, 100) # (2^10) % 100
print(result) # Output: 24
# This is more efficient than computing separately for large numbers:
# result = (2 ** 10) % 100 # Same result, but less efficient for large numbersPour la plupart des usages quotidiens, l’opérateur ** est plus pratique que pow() :
# practical_exponentiation.py
# Calculate compound interest: A = P(1 + r)^t
principal = 1000 # Initial amount
rate = 0.05 # 5% interest rate
years = 10
amount = principal * (1 + rate) ** years
print(f"Amount after {years} years: ${amount:.2f}")
# Output: Amount after 10 years: $1628.89
# Calculate area of a square
side_length = 5
area = side_length ** 2
print(f"Area: {area}") # Output: Area: 25
# Calculate volume of a cube
side_length = 3
volume = side_length ** 3
print(f"Volume: {volume}") # Output: Volume: 274.6.4) Combiner les fonctions intégrées
Ces fonctions fonctionnent bien ensemble pour résoudre des problèmes courants :
# combining_functions.py
# Find the range (difference between max and min)
values = [15, 42, 8, 23, 37]
value_range = max(values) - min(values)
print(f"Range: {value_range}") # Output: Range: 34
# Note: We're using a list here (we'll learn about lists in detail in Chapter 13)
# For now, just understand that max() and min() can work with a list of values
# Calculate distance between two points in 2D space
x1, y1 = 3, 4
x2, y2 = 6, 8
# Distance formula: sqrt((x2-x1)^2 + (y2-y1)^2)
# We'll use ** for squaring (square root comes in section 4.11)
distance_squared = (x2 - x1) ** 2 + (y2 - y1) ** 2
distance = distance_squared ** 0.5 # Square root via fractional exponent
print(f"Distance: {distance}") # Output: Distance: 5.0Ces fonctions intégrées sont des outils fondamentaux en programmation Python. Elles sont efficaces, bien testées et fonctionnent de manière cohérente avec différents types numériques. Utilisez‑les chaque fois que vous avez besoin de ces opérations courantes — il est inutile d’écrire vos propres implémentations.
4.7) Arrondir les nombres avec round()
Lorsque vous travaillez avec des nombres à virgule flottante, vous avez souvent besoin d’arrondir les résultats à un certain nombre de décimales pour l’affichage, les calculs ou le stockage. La fonction round() de Python fournit cette fonctionnalité.
4.7.1) Arrondi de base avec round()
La fonction round() prend un nombre et l’arrondit à l’entier le plus proche :
# basic_rounding.py
# Round to nearest integer
result = round(3.7)
print(result) # Output: 4
result = round(3.2)
print(result) # Output: 3
# Exactly halfway rounds to nearest even number (banker's rounding)
result = round(2.5)
print(result) # Output: 2 (rounds to even)
result = round(3.5)
print(result) # Output: 4 (rounds to even)
# Negative numbers work too
result = round(-3.7)
print(result) # Output: -4
result = round(-3.2)
print(result) # Output: -3Remarquez le comportement lorsque vous arrondissez des nombres exactement à mi‑chemin entre deux entiers (comme 2.5 ou 3.5). Python utilise l’arrondi « au pair » (round half to even, ou « banker's rounding »), qui arrondit vers l’entier pair le plus proche. Cela réduit le biais dans les arrondis répétés. Pour la plupart des usages quotidiens, ce détail compte peu, mais il est utile de le connaître.
4.7.2) Arrondir à un nombre spécifique de décimales
La fonction round() accepte un second argument optionnel indiquant combien de décimales conserver :
# decimal_places.py
# Round to 2 decimal places
result = round(3.14159, 2)
print(result) # Output: 3.14
# Round to 1 decimal place
result = round(3.14159, 1)
print(result) # Output: 3.1
# Round to 3 decimal places
result = round(2.71828, 3)
print(result) # Output: 2.718
# You can round to 0 decimal places (same as omitting the argument)
result = round(3.7, 0)
print(result) # Output: 4.0 (note: returns float, not int)Lorsque vous spécifiez le nombre de décimales, round() renvoie un float (même si vous arrondissez à 0 décimale). Quand vous omettez le second argument, il renvoie un entier.
4.7.3) Usages pratiques de l’arrondi
L’arrondi est essentiel pour afficher des montants d’argent, des mesures et d’autres valeurs où une précision excessive est inutile ou déroutante :
# practical_rounding.py
# Display prices with 2 decimal places
price = 19.99
tax_rate = 0.08
total = price * (1 + tax_rate)
print(f"Total (unrounded): ${total}") # Output: Total (unrounded): $21.5892
print(f"Total (rounded): ${round(total, 2)}") # Output: Total (rounded): $21.59
# Calculate and display average
total_score = 456
num_tests = 7
average = total_score / num_tests
print(f"Average (unrounded): {average}") # Output: Average (unrounded): 65.14285714285714
print(f"Average (rounded): {round(average, 2)}") # Output: Average (rounded): 65.14
# Round measurements to reasonable precision
distance_meters = 123.456789
distance_rounded = round(distance_meters, 1)
print(f"Distance: {distance_rounded} meters") # Output: Distance: 123.5 meters4.7.4) Arrondir vs tronquer vs plancher/plafond
Il est important de comprendre que l’arrondi est différent de la troncature (suppression des décimales) ou des opérations de plancher/plafond (floor/ceil) :
# rounding_vs_others.py
value = 3.7
# Rounding: nearest integer
rounded = round(value)
print(f"Rounded: {rounded}") # Output: Rounded: 4
# Truncating: remove decimal part (convert to int)
truncated = int(value)
print(f"Truncated: {truncated}") # Output: Truncated: 3
# We'll see floor and ceil in section 4.11, but briefly:
# Floor: largest integer <= value (always rounds down)
# Ceiling: smallest integer >= value (always rounds up)Pour les nombres négatifs, les différences sont plus marquées :
# negative_rounding.py
value = -3.7
# Rounding: nearest integer
rounded = round(value)
print(f"Rounded: {rounded}") # Output: Rounded: -4
# Truncating: toward zero
truncated = int(value)
print(f"Truncated: {truncated}") # Output: Truncated: -3
# Floor (rounds down toward negative infinity): -4
# Ceiling (rounds up toward positive infinity): -34.7.5) Points importants concernant l’arrondi
La précision des floats peut provoquer des surprises :
En raison de la façon dont les ordinateurs représentent les nombres à virgule flottante (ce que nous verrons en section 4.10), l’arrondi ne donne pas toujours exactement le résultat attendu :
# rounding_surprises.py
# Sometimes rounding doesn't give the exact decimal you expect
value = 2.675
rounded = round(value, 2)
print(rounded) # Output: 2.67 (not 2.68 as you might expect)
# This happens because 2.675 can't be represented exactly in binary floating-point
# The actual stored value is slightly less than 2.675Pour la plupart des usages pratiques, ce n’est pas un problème. Mais si vous travaillez sur des calculs financiers où l’arithmétique décimale exacte est cruciale, vous pourriez avoir besoin du module decimal de Python (que nous ne couvrirons pas dans ce livre, mais il est utile de savoir qu’il existe).
Arrondir pour l’affichage vs pour le calcul :
Souvent, vous souhaitez arrondir uniquement pour l’affichage, en conservant la pleine précision pour les calculs :
# rounding_for_display.py
price1 = 19.99
price2 = 15.49
tax_rate = 0.08
# Calculate with full precision
subtotal = price1 + price2
tax = subtotal * tax_rate
total = subtotal + tax
# Round only for display
print(f"Subtotal: ${round(subtotal, 2)}") # Output: Subtotal: $35.48
print(f"Tax: ${round(tax, 2)}") # Output: Tax: $2.84
print(f"Total: ${round(total, 2)}") # Output: Total: $38.32
# The variables still contain full precision
print(f"Total (full precision): ${total}")
# Output: Total (full precision): $38.3184La fonction round() est l’une des fonctions intégrées les plus utilisées de Python. Utilisez‑la chaque fois que vous devez présenter des résultats numériques dans un format lisible ou lorsque vous devez limiter la précision pour un objectif spécifique.
4.8) Schémas numériques courants (compteurs, totaux, moyennes)
Maintenant que vous comprenez les opérations numériques et les fonctions de Python, explorons des schémas courants que vous utiliserez sans cesse dans de vrais programmes. Ces schémas constituent les blocs de base pour traiter des données numériques.
4.8.1) Compteurs : suivre combien de fois
Un compteur est une variable qui suit combien de fois quelque chose arrive. Vous l’initialisez à zéro et vous l’incrémentez à chaque fois qu’un événement se produit :
# basic_counter.py
# Count how many numbers we've processed
count = 0 # Initialize counter
# Process first number
count += 1
print(f"Processed {count} number(s)") # Output: Processed 1 number(s)
# Process second number
count += 1
print(f"Processed {count} number(s)") # Output: Processed 2 number(s)
# Process third number
count += 1
print(f"Processed {count} number(s)") # Output: Processed 3 number(s)Dans de vrais programmes, vous utiliseriez typiquement des compteurs à l’intérieur de boucles (loops) (que nous verrons aux chapitres 10 et 11). Pour l’instant, comprenez le schéma : partir de zéro, ajouter un à chaque fois.
Les compteurs peuvent suivre différents types d’événements :
# multiple_counters.py
# Track different categories
even_count = 0
odd_count = 0
# Check number 1
number = 4
if number % 2 == 0:
even_count += 1
else:
odd_count += 1
# Check number 2
number = 7
if number % 2 == 0:
even_count += 1
else:
odd_count += 1
# Check number 3
number = 10
if number % 2 == 0:
even_count += 1
else:
odd_count += 1
print(f"Even numbers: {even_count}") # Output: Even numbers: 2
print(f"Odd numbers: {odd_count}") # Output: Odd numbers: 14.8.2) Accumulateurs : construire des totaux
Un accumulateur est une variable qui collecte un total cumulatif. Comme un compteur, vous l’initialisez à zéro, mais au lieu d’ajouter un à chaque fois, vous ajoutez des montants variables :
# basic_accumulator.py
# Calculate total sales
total_sales = 0 # Initialize accumulator
# First sale
sale = 19.99
total_sales += sale
print(f"Total so far: ${total_sales}") # Output: Total so far: $19.99
# Second sale
sale = 34.50
total_sales += sale
print(f"Total so far: ${total_sales}") # Output: Total so far: $54.49
# Third sale
sale = 12.00
total_sales += sale
print(f"Total so far: ${total_sales}") # Output: Total so far: $66.49Les accumulateurs sont fondamentaux pour le traitement de données. Ils vous permettent d’agréger des valeurs au fur et à mesure de leur traitement :
# multiple_accumulators.py
# Track both total and count to calculate average later
total_score = 0
count = 0
# Process score 1
score = 85
total_score += score
count += 1
# Process score 2
score = 92
total_score += score
count += 1
# Process score 3
score = 78
total_score += score
count += 1
print(f"Total score: {total_score}") # Output: Total score: 255
print(f"Number of scores: {count}") # Output: Number of scores: 34.8.3) Calculer des moyennes
Une moyenne combine les schémas de compteur et d’accumulateur : vous avez besoin à la fois d’un total (accumulateur) et d’un compte (compteur), puis vous divisez :
# calculating_average.py
# Calculate average of test scores
total_score = 0
count = 0
# Add scores
total_score += 85
count += 1
total_score += 92
count += 1
total_score += 78
count += 1
total_score += 88
count += 1
# Calculate average
average = total_score / count
print(f"Average score: {average}") # Output: Average score: 85.75
# Often you'll want to round the average
average_rounded = round(average, 2)
print(f"Average (rounded): {average_rounded}") # Output: Average (rounded): 85.75Important : vérifiez toujours la division par zéro :
Lorsque vous calculez des moyennes, vous devez vous assurer que le compteur n’est pas zéro :
# safe_average.py
total_score = 0
count = 0
# If no scores were added, count is still 0
# Dividing by zero causes an error!
if count > 0:
average = total_score / count
print(f"Average: {average}")
else:
print("No scores to average")
# Output: No scores to averageNous verrons davantage comment gérer ce type de conditions au chapitre 8.
4.8.4) Suivre le maximum et le minimum au fil du temps
Parfois vous devez suivre la plus grande ou la plus petite valeur vue jusqu’à présent :
Vous pouvez utiliser les fonctions max() et min() pour implémenter cela :
# running_max_min_simplified.py
# Track highest and lowest using max() and min()
highest_temp = 72
lowest_temp = 72
# Update with new temperature
current_temp = 85
highest_temp = max(highest_temp, current_temp)
lowest_temp = min(lowest_temp, current_temp)
# Update with another temperature
current_temp = 68
highest_temp = max(highest_temp, current_temp)
lowest_temp = min(lowest_temp, current_temp)
print(f"High: {highest_temp}, Low: {lowest_temp}")
# Output: High: 85, Low: 684.9) Opérateurs au niveau des bits (bitwise) pour les entiers : &, |, ^, <<, >> (aperçu rapide)
Les opérateurs au niveau des bits agissent sur les bits individuels (chiffres binaires) des entiers. Vous n’utiliserez pas ces opérateurs aussi souvent que les opérateurs arithmétiques dans la programmation quotidienne, mais ils sont importants pour certaines tâches comme la gestion de fanions (flags), de permissions, la manipulation de données bas niveau et l’optimisation des performances.
Cette section fournit un bref aperçu. Comprendre les opérations au niveau des bits suppose de comprendre la représentation binaire, qui est un sujet plus profond que ce que nous pouvons explorer dans ce chapitre d’introduction.
4.9.1) Comprendre la représentation binaire (introduction rapide)
Les ordinateurs stockent les entiers comme des séquences de bits (0 et 1). Par exemple :
- Le décimal 5 est le binaire
101(1×4 + 0×2 + 1×1) - Le décimal 12 est le binaire
1100(1×8 + 1×4 + 0×2 + 0×1)
La fonction bin() de Python montre la représentation binaire d’un entier :
# binary_representation.py
# See binary representation of integers
print(bin(5)) # Output: 0b101
print(bin(12)) # Output: 0b1100
print(bin(255)) # Output: 0b11111111
# The 0b prefix indicates binary notation4.9.2) ET binaire (bitwise AND) avec &
L’opérateur & effectue un ET au niveau des bits : chaque bit du résultat vaut 1 uniquement si les bits correspondants des deux opérandes valent 1 :
# bitwise_and.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 & 10
print(result) # Output: 8
print(bin(result)) # Output: 0b1000
# How it works:
# 1100 (12)
# & 1010 (10)
# ------
# 1000 (8)Le ET binaire est souvent utilisé pour vérifier si certains bits sont activés (test de fanions/flags) :
# checking_flags.py
# File permissions example (simplified)
READ = 4 # 100 in binary
WRITE = 2 # 010 in binary
EXECUTE = 1 # 001 in binary
permissions = 6 # 110 in binary (READ + WRITE)
# Check if READ permission is set
has_read = (permissions & READ) != 0
print(f"Has read: {has_read}") # Output: Has read: True
# Check if EXECUTE permission is set
has_execute = (permissions & EXECUTE) != 0
print(f"Has execute: {has_execute}") # Output: Has execute: False4.9.3) OU binaire (bitwise OR) avec |
L’opérateur | effectue un OU au niveau des bits : chaque bit du résultat vaut 1 si le bit correspondant dans au moins un des opérandes vaut 1 :
# bitwise_or.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 | 10
print(result) # Output: 14
print(bin(result)) # Output: 0b1110
# How it works:
# 1100 (12)
# | 1010 (10)
# ------
# 1110 (14)Le OU binaire est utilisé pour combiner des fanions :
# combining_flags.py
READ = 4 # 100 in binary
WRITE = 2 # 010 in binary
EXECUTE = 1 # 001 in binary
# Grant READ and WRITE permissions
permissions = READ | WRITE
print(f"Permissions: {permissions}") # Output: Permissions: 6
print(bin(permissions)) # Output: 0b110
# Add EXECUTE permission
permissions = permissions | EXECUTE
print(f"Permissions: {permissions}") # Output: Permissions: 7
print(bin(permissions)) # Output: 0b1114.9.4) XOR binaire (bitwise XOR) avec ^
L’opérateur ^ effectue un XOR (OU exclusif) au niveau des bits : chaque bit du résultat vaut 1 si les bits correspondants des opérandes sont différents :
# bitwise_xor.py
# 12 is 1100 in binary
# 10 is 1010 in binary
result = 12 ^ 10
print(result) # Output: 6
print(bin(result)) # Output: 0b110
# How it works:
# 1100 (12)
# ^ 1010 (10)
# ------
# 0110 (6)Le XOR a des propriétés intéressantes, comme le fait d’inverser (toggle) des bits ou d’échanger des valeurs :
# xor_properties.py
# XOR with itself gives 0
result = 5 ^ 5
print(result) # Output: 0
# XOR with 0 gives the original number
result = 5 ^ 0
print(result) # Output: 5
# XOR is its own inverse (useful for simple encryption)
original = 42
key = 123
encrypted = original ^ key
decrypted = encrypted ^ key
print(f"Original: {original}, Encrypted: {encrypted}, Decrypted: {decrypted}")
# Output: Original: 42, Encrypted: 81, Decrypted: 424.9.5) Décalages à gauche (<<) et à droite (>>)
L’opérateur << décale les bits vers la gauche, et >> décale les bits vers la droite :
# bit_shifting.py
# Left shift: multiply by powers of 2
result = 5 << 1 # Shift left by 1 bit
print(result) # Output: 10
# 5 is 101 in binary, shifted left becomes 1010 (10)
result = 5 << 2 # Shift left by 2 bits
print(result) # Output: 20
# 5 is 101 in binary, shifted left becomes 10100 (20)
# Right shift: divide by powers of 2 (floor division)
result = 20 >> 1 # Shift right by 1 bit
print(result) # Output: 10
# 20 is 10100 in binary, shifted right becomes 1010 (10)
result = 20 >> 2 # Shift right by 2 bits
print(result) # Output: 5
# 20 is 10100 in binary, shifted right becomes 101 (5)Décaler à gauche de n bits multiplie par 2^n, et décaler à droite divise par 2^n (division entière) :
# shift_as_multiplication.py
# Left shift multiplies by powers of 2
print(3 << 1) # Output: 6 (3 * 2^1 = 3 * 2)
print(3 << 2) # Output: 12 (3 * 2^2 = 3 * 4)
print(3 << 3) # Output: 24 (3 * 2^3 = 3 * 8)
# Right shift divides by powers of 2
print(24 >> 1) # Output: 12 (24 // 2^1 = 24 // 2)
print(24 >> 2) # Output: 6 (24 // 2^2 = 24 // 4)
print(24 >> 3) # Output: 3 (24 // 2^3 = 24 // 8)4.9.6) Quand utiliser les opérateurs bitwise
Les opérateurs bitwise sont utiles dans des scénarios spécifiques :
- Travail avec des fanions binaires et des permissions (comme les permissions de fichiers sous Unix/Linux)
- Manipulation de données bas niveau (regrouper plusieurs valeurs dans un seul entier)
- Programmation réseau (manipuler des adresses IP, des fanions de protocole)
- Programmation graphique et jeux (manipulation de couleurs, détection de collisions)
- Optimisation des performances (le décalage de bits est plus rapide que la multiplication/division par des puissances de 2)
Pour la plupart des tâches de programmation quotidiennes, vous n’aurez pas besoin des opérateurs bitwise. Mais lorsque vous en aurez besoin — en particulier pour la programmation système, le réseau ou le code critique en performance — ce sont des outils précieux.
Remarque : Cet aperçu couvre les bases. Les opérations bitwise deviennent plus complexes avec les nombres négatifs (impliquant la représentation en complément à deux), ce qui dépasse la portée de cette introduction. Pour l’instant, concentrez‑vous sur le fait que ces opérateurs existent et sur ce qu’ils font à un niveau général.
4.10) Précision des nombres flottants et erreurs d’arrondi (explication simple)
Les nombres à virgule flottante en Python (et dans la plupart des langages de programmation) peuvent représenter une très large plage de valeurs, de minuscules fractions à d’énormes nombres. Cependant, ils ont une limitation qui peut surprendre les débutants : ils ne peuvent pas représenter exactement tous les nombres décimaux. Cette section explique pourquoi cela se produit et ce que cela signifie pour vos programmes.
4.10.1) Pourquoi les nombres flottants ne sont pas toujours exacts
Les ordinateurs stockent les nombres à virgule flottante en binaire (base 2), et non en décimal (base 10). Certaines fractions décimales qui paraissent simples pour nous ne peuvent pas être représentées exactement en binaire, tout comme 1/3 ne peut pas être représenté exactement en décimal (0,333333… continue à l’infini).
Voici un exemple surprenant :
# floating_point_surprise.py
# This seems like it should be exactly 0.3
result = 0.1 + 0.2
print(result) # Output: 0.30000000000000004 (not exactly 0.3!)
# Check if it equals 0.3
print(result == 0.3) # Output: FalseCe n’est pas un bug de Python — c’est une limitation fondamentale de la façon dont les ordinateurs représentent les nombres à virgule flottante. Le nombre décimal 0.1 ne peut pas être représenté exactement en binaire flottant, tout comme 1/3 ne peut pas être représenté exactement en décimal.
4.10.2) Comprendre le problème de représentation
Voyons plus d’exemples de ce comportement :
# more_precision_examples.py
# Simple decimal values that aren't exact in binary
print(0.1) # Output: 0.1 (Python rounds for display)
print(repr(0.1)) # Output: 0.1 (still rounded)
# But the actual stored value has tiny errors
print(0.1 + 0.1 + 0.1) # Output: 0.30000000000000004
# Multiplication can accumulate these errors
result = 0.1 * 3
print(result) # Output: 0.30000000000000004
# Some numbers are exact (powers of 2)
print(0.5) # Output: 0.5 (exact)
print(0.25) # Output: 0.25 (exact)
print(0.125) # Output: 0.125 (exact)Les nombres qui sont des puissances de 2 (comme 0.5, 0.25, 0.125) ou des sommes de puissances de 2 peuvent être représentés exactement. Mais la plupart des fractions décimales ne le peuvent pas.
4.10.3) Implications pratiques
Pour la plupart des programmes du quotidien, ces petites erreurs ne sont pas importantes. Mais il existe des situations où vous devez en être conscient :
Comparer des nombres flottants :
# comparing_floats.py
# Direct equality comparison can fail
a = 0.1 + 0.2
b = 0.3
print(a == b) # Output: False (due to tiny difference)
# Better: check if they're close enough
difference = abs(a - b)
tolerance = 0.0001 # How close is "close enough"?
print(difference < tolerance) # Output: True (they're close enough)
# Python 3.5+ provides math.isclose() for this (we'll see in section 4.11)Accumulation d’erreurs dans les calculs répétés :
# accumulated_errors.py
# Adding 0.1 ten times
total = 0.0
for i in range(10):
total += 0.1
print(total) # Output: 0.9999999999999999 (not exactly 1.0)
print(total == 1.0) # Output: False
# The error accumulates with each additionCalculs financiers :
# financial_calculations.py
# Money calculations can have surprising results
price = 0.10
quantity = 3
total = price * quantity
print(total) # Output: 0.30000000000000004
# For financial calculations, consider rounding to cents
total_cents = round(total * 100) # Convert to cents
total_dollars = total_cents / 100
print(total_dollars) # Output: 0.3
# Or use Python's decimal module for exact decimal arithmetic
# (beyond the scope of this chapter, but worth knowing about)4.10.4) Stratégies pour travailler avec les nombres flottants
Arrondir les résultats pour l’affichage :
# rounding_for_display.py
result = 0.1 + 0.2
print(f"Result: {round(result, 2)}") # Output: Result: 0.3
# Or use formatted output (we'll learn more in Chapter 6)
print(f"Result: {result:.2f}") # Output: Result: 0.30Ne comparez pas les floats pour une égalité exacte :
# safe_float_comparison.py
a = 0.1 + 0.2
b = 0.3
# Instead of: if a == b:
# Use: if they're close enough
if abs(a - b) < 0.0001:
print("Close enough to equal")
# Output: Close enough to equalSoyez attentif lorsque la précision est importante :
Pour le calcul scientifique, les calculs financiers ou tout domaine où l’arithmétique décimale exacte est cruciale, vous pourriez avoir besoin :
- Du module
decimalde Python pour l’arithmétique décimale exacte - Du module
fractionsde Python pour l’arithmétique rationnelle exacte - D’un arrondi soigneux à des moments appropriés dans vos calculs
Ces modules dépassent la portée de ce chapitre, mais il est utile de savoir qu’ils existent.
4.10.5) Quand la précision des flottants n’a pas d’importance
Pour la plupart des tâches de programmation quotidiennes, la précision des nombres à virgule flottante est largement suffisante :
# when_precision_is_fine.py
# Calculating area
length = 5.5
width = 3.2
area = length * width
print(f"Area: {area:.2f} square meters") # Output: Area: 17.60 square meters
# Converting temperature
fahrenheit = 98.6
celsius = (fahrenheit - 32) * 5 / 9
print(f"Temperature: {celsius:.1f}°C") # Output: Temperature: 37.0°C
# Calculating average
total = 456.78
count = 7
average = total / count
print(f"Average: {average:.2f}") # Output: Average: 65.25Lorsque vous arrondissez les résultats pour l’affichage ou lorsque les minuscules erreurs sont insignifiantes par rapport à votre précision de mesure, l’arithmétique en virgule flottante fonctionne parfaitement bien.
4.10.6) L’essentiel à retenir
Les nombres flottants sont des approximations de nombres réels. Pour la plupart des tâches de programmation, ils sont suffisamment précis. Mais retenez que :
- Ne comparez pas les floats pour une égalité exacte — vérifiez plutôt s’ils sont suffisamment proches
- Arrondissez les résultats lors de l’affichage pour éviter de montrer une précision dénuée de sens
- Soyez conscient de l’accumulation d’erreurs dans les calculs répétés
- Pour les calculs financiers, envisagez un arrondi à la précision appropriée ou l’utilisation du module
decimal
Comprendre ces limitations vous aide à écrire des programmes plus robustes et à éviter des bugs surprenants. La bonne nouvelle est que, pour la grande majorité des tâches de programmation, l’arithmétique en virgule flottante « fonctionne tout simplement » si vous suivez ces conseils simples.
4.11) (Optionnel) Fonctions mathématiques de base avec le module math (sqrt, floor, ceil, pi)
Les opérateurs et fonctions intégrés de Python couvrent l’arithmétique de base, mais pour des opérations mathématiques plus avancées, Python propose le module math. Un module est une collection de fonctions et de valeurs liées que vous pouvez utiliser dans vos programmes. Nous verrons beaucoup plus de choses sur les modules au chapitre 22, mais pour l’instant nous allons présenter les bases nécessaires pour utiliser le module math.
4.11.1) Importer le module math
Pour utiliser le module math, vous devez l’importer au début de votre programme :
# importing_math.py
import math
# Now you can use functions from the math module
# by writing math.function_name()Lorsque vous importez un module, vous avez accès à toutes ses fonctions et valeurs. Vous les utilisez en écrivant le nom du module, un point, puis le nom de la fonction ou de la valeur.
4.11.2) Constantes mathématiques : pi et e
Le module math fournit des valeurs précises pour des constantes mathématiques importantes :
# math_constants.py
import math
# Pi (π): ratio of circle's circumference to diameter
print(math.pi) # Output: 3.141592653589793
# Euler's number (e): base of natural logarithms
print(math.e) # Output: 2.718281828459045
# Using pi to calculate circle properties
radius = 5
circumference = 2 * math.pi * radius
area = math.pi * radius ** 2
print(f"Circumference: {circumference:.2f}") # Output: Circumference: 31.42
print(f"Area: {area:.2f}") # Output: Area: 78.544.11.3) Racine carrée avec sqrt()
La fonction sqrt() calcule la racine carrée d’un nombre :
# square_root.py
import math
# Square root of perfect squares
result = math.sqrt(16)
print(result) # Output: 4.0
result = math.sqrt(25)
print(result) # Output: 5.0
# Square root of non-perfect squares
result = math.sqrt(2)
print(result) # Output: 1.4142135623730951
# Using sqrt in calculations
# Distance formula: sqrt((x2-x1)^2 + (y2-y1)^2)
x1, y1 = 3, 4
x2, y2 = 6, 8
distance = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
print(f"Distance: {distance}") # Output: Distance: 5.0Notez que sqrt() renvoie un float, même pour les carrés parfaits. De plus, vous ne pouvez pas prendre la racine carrée de nombres négatifs avec sqrt() (cela déclencherait une erreur).
4.11.4) Plancher et plafond avec floor() et ceil()
La fonction floor() arrondit à l’entier inférieur le plus proche (vers moins l’infini), et ceil() arrondit vers l’entier supérieur le plus proche (vers plus l’infini) :
# floor_and_ceil.py
import math
# Floor: rounds down
result = math.floor(3.7)
print(result) # Output: 3
result = math.floor(3.2)
print(result) # Output: 3
# Ceiling: rounds up
result = math.ceil(3.7)
print(result) # Output: 4
result = math.ceil(3.2)
print(result) # Output: 4
# With negative numbers
result = math.floor(-3.7)
print(result) # Output: -4 (rounds toward negative infinity)
result = math.ceil(-3.7)
print(result) # Output: -3 (rounds toward positive infinity)Ces fonctions sont utiles lorsque vous devez garantir qu’une valeur se trouve dans une certaine plage ou lorsque vous avez besoin de résultats entiers avec un comportement d’arrondi spécifique :
# practical_floor_ceil.py
import math
# How many boxes needed to pack all items?
items = 47
items_per_box = 12
# Use ceil to round up (need a full box even if not completely filled)
boxes_needed = math.ceil(items / items_per_box)
print(f"Boxes needed: {boxes_needed}") # Output: Boxes needed: 4
# How many complete pages for a document?
total_lines = 250
lines_per_page = 30
# Use floor to count only complete pages
complete_pages = math.floor(total_lines / lines_per_page)
print(f"Complete pages: {complete_pages}") # Output: Complete pages: 84.11.5) Comparer floor(), ceil(), round() et int()
Il est utile de comprendre comment ces différentes approches d’arrondi diffèrent :
# comparing_rounding_methods.py
import math
value = 3.7
print(f"Original: {value}")
print(f"floor(): {math.floor(value)}") # Output: floor(): 3
print(f"ceil(): {math.ceil(value)}") # Output: ceil(): 4
print(f"round(): {round(value)}") # Output: round(): 4
print(f"int(): {int(value)}") # Output: int(): 3
# With negative numbers, the differences are more apparent
value = -3.7
print(f"\nOriginal: {value}")
print(f"floor(): {math.floor(value)}") # Output: floor(): -4
print(f"ceil(): {math.ceil(value)}") # Output: ceil(): -3
print(f"round(): {round(value)}") # Output: round(): -4
print(f"int(): {int(value)}") # Output: int(): -3Voici une représentation visuelle du comportement de ces fonctions :
4.11.6) Autres fonctions utiles du module math
Le module math contient de nombreuses autres fonctions. En voici quelques‑unes qui sont souvent utiles :
# other_math_functions.py
import math
# Absolute value (also available as built-in abs())
result = math.fabs(-5.5)
print(result) # Output: 5.5
# Power function (also available as ** operator)
result = math.pow(2, 3)
print(result) # Output: 8.0
# Trigonometric functions (angles in radians)
result = math.sin(math.pi / 2) # sin(90 degrees)
print(result) # Output: 1.0
result = math.cos(0) # cos(0 degrees)
print(result) # Output: 1.0
# Logarithms
result = math.log(math.e) # Natural log (base e)
print(result) # Output: 1.0
result = math.log10(100) # Log base 10
print(result) # Output: 2.0
# Check if a float is close to another (Python 3.5+)
a = 0.1 + 0.2
b = 0.3
result = math.isclose(a, b)
print(result) # Output: TrueLa fonction isclose() est particulièrement utile pour comparer des nombres flottants (comme nous l’avons évoqué en section 4.10) :
# isclose_example.py
import math
# Instead of direct equality comparison
a = 0.1 + 0.2
b = 0.3
# Don't do this:
# if a == b: # This would be False
# Do this instead:
if math.isclose(a, b):
print("Values are close enough")
# Output: Values are close enough
# You can specify the tolerance
if math.isclose(a, b, rel_tol=1e-9): # Very strict tolerance
print("Values are very close")
# Output: Values are very close4.11.7) Quand utiliser le module math
Utilisez le module math lorsque vous avez besoin :
- De constantes mathématiques (π, e)
- De racines carrées et autres racines
- De fonctions trigonométriques (sin, cos, tan)
- De fonctions logarithmiques et exponentielles
- D’un contrôle précis de l’arrondi (floor, ceil)
- De comparaison de nombres flottants (
isclose)
Pour l’arithmétique de base (+, -, *, /, //, %, **), utilisez les opérateurs intégrés de Python. Pour des opérations mathématiques plus avancées, importez et utilisez le module math.
Le module math fait partie de la bibliothèque standard de Python, ce qui signifie qu’il est toujours disponible — vous devez simplement l’importer. Nous explorerons d’autres modules et apprendrons le système d’importation en détail au chapitre 22.
Dans ce chapitre, vous avez appris à travailler avec les nombres en Python : effectuer des opérations arithmétiques, comprendre les différents types de division, utiliser la priorité des opérateurs, mélanger entiers et floats, employer les fonctions numériques intégrées, reconnaître des schémas numériques courants, travailler avec les opérateurs bitwise, comprendre la précision des flottants et utiliser le module math pour des opérations avancées.
Ces opérations numériques constituent la base d’innombrables tâches de programmation, des calculs simples à l’analyse de données complexe. À mesure que vous continuez à apprendre Python, vous utiliserez ces opérations en permanence, souvent en combinaison avec les structures de contrôle et les collections de données que vous découvrirez dans les chapitres suivants.
Entraînez‑vous avec ces opérations jusqu’à ce qu’elles deviennent naturelles. Essayez d’écrire de petits programmes qui calculent des quantités réelles : convertir des températures, calculer des aires et des volumes, traiter des données financières ou analyser des mesures. Plus vous pratiquerez, plus vous serez à l’aise avec les capacités numériques de Python.