Informatique et Calcul Scientifique

Cours 1

Author

Leonard Hardiman et Ghid Maatouk

Objectifs de la leçon

Le but de la leçon d’aujourd’hui est de :

  • Faire connaissance et présenter le déroulement du cours
  • Introduire la notion de type et de casting
  • La fonction print() pour afficher des informations
  • Comment stocker les données : les variables
  • Introduction aux fonctions en Python

Déroulement du cours : Moodle

Toutes les informations importantes se trouvent sur le Moodle du cours.

  • Des anciens enregistrements de cours sont disponibles sur la chaı̂ne dédiée.
  • Les exercices auront lieu chaque mercredi de 10h15 à 12h00, dans les salles suivantes :

CO 020, CO 023, CO 4, CO 5 (avec ordinateurs)

INF 1, INF 2 (ordis personnels)

  • Ils prendront la forme de travaux pratiques sur ordinateur et sont accessibles sur la plateforme Noto de l’Epfl.
  • N’hésitez pas à poser vos questions sur le forum du cours !

Plan du cours

Le semestre d’automne est séparé en deux parties de 6 semaines chacune :

  • Introduction au langage Python
  • Introduction à l’algorithmique

Aucun prérequis n’est demandé !

La matière sera évaluée à la fin de chaque partie.

Qu’est-ce que l’informatique ?

Définition de l’informatique

Théorie et traitement de l’information à l’aide de programmes mis en œuvre sur ordinateurs. (Le Robert, 2024)

  • En informatique, l’information est représentée par un ensemble de données.
  • Un ordinateur est un automate programmé effectuant des opérations.
  • Un programme est une méthode de communication avec la machine pour lui demander d’effectuer des tâches.

Communication avec l’ordinateur

  • L’ordinateur parle le langage machine. Il s’agit d’une suite de bits (1 et 0) qui est difficile à comprendre pour un être humain.
  • Pour communiquer avec la machine, on utilise un langage de programmation.

Le langage Python

Le langage que nous allons utiliser dans le cadre de ce cours est le langage Python, car il est…

  • …populaire, il possède donc une multitude de librairies et modules.
  • …intuitif, sa syntaxe claire évite du code superflu.
  • …interprété, ce qui permet de mieux comprendre où l’ordinateur se trouve dans le code lors de l’exécution.
  • …gratuit, comme tous les langages… on les paie en santé mentale !

Dans ce cours, nous programmerons dans l’environnement de développement intégré (IDE) Jupyter Notebook sur la plateforme Noto.

Comment marche Python

Python exécute les instructions ligne par ligne, de haut en bas.

print('Ligne 1')
print('Ligne 2')
print('Ligne 3')
Ligne 1
Ligne 2
Ligne 3
  • Il exécute immédiatement chaque instruction rencontrée
  • Les commentaires (précédés de #) sont ignorés, même au milieu d’une ligne
print('Ligne 1')
# print('Ligne 2')  # Ceci est ignoré
print('Ligne 3')  # Tout après # est un commentaire
Ligne 1
Ligne 3

Remarque. Ce comportement peut être observé en exécutant Python avec un débogueur (debugger).

Comment utiliser Python

Comme mentionné précédemment, un programme informatique agit sur un ensemble de données (appelé input data).

Un bon programme est réutilisable un nombre arbitraire de fois: les données d’entrée changent, mais la même fonctionnalité est effectuée.

La manière standard de structurer un programme consiste à définir d’abord les fonctions, qui ne dépendent pas des données…

def fonction(x):
    [...]
    return y

fonction(input_data)

…puis à appeler ces fonctions sur les données.

Un premier exemple

Illustrons avec une bonne et une mauvaise façon d’écrire un programme. Supposons que quelqu’un nous demande de calculer l’aire d’un triangle avec une base de 9 et une hauteur de 3. Comme nous connaissons la formule

\[\text{Aire d'un triangle} = \dfrac{\text{base} \times \text{hauteur}}{2},\]

une façon de répondre à cette demande serait de traiter Python comme une calculatrice et simplement calculer.

print('L\'aire vaut', 9 * 3 / 2)
L'aire vaut 13.5

Mais, dans la plupart des cas, il est préférable d’écrire un autre type de programme. Un qui comprend la fonctionnalité que nous essayons d’implémenter et ensuite utilise cette fonctionnalité sur les données d’entrée.

def aire_triangle(base,hauteur):
    return base * hauteur / 2 

print('L\'aire vaut', aire_triangle(9,3))
L'aire vaut 13.5

On peut changer les données d’entrée:

def aire_triangle(base,hauteur):
    return base * hauteur / 2 

print('L\'aire vaut', aire_triangle(12,5))
L'aire vaut 30.0

Ici, nous utilisons une fonction pour implémenter la fonctionnalité, avant de l’appliquer. Nous verrons comment construire une fonction dans un instant. Pour l’instant, parlons moins des données d’entrée, et plus généralement des données dans Python.

Remarque. Les données ne sont pas forcément des nombres!

Types dans Python

Les données dans Python sont portées par des objets. Chaque objet a un type qui détermine sa structure et les opérations qu’on peut effectuer sur lui.

print(type(42))
<class 'int'>
print(type(12.3))
<class 'float'>
print(type('Appelez-moi Ishmael'))
<class 'str'>
print(print('Appelez-moi Ishmael' + 4))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[9], line 1
----> 1 print(print('Appelez-moi Ishmael' + 4))

TypeError: can only concatenate str (not "int") to str
print(type(True))
<class 'bool'>
print(type(None))
<class 'NoneType'>
print(type(type(None)))
<class 'type'>

Remarque. Les int et les float sont tous deux des types numériques.

Opérations sur les types numériques

  • Opérations arithmétiques usuelles : +, -, * et /.
print(4+12)
16
print(3-9)
-6
print(22*3)
66
print(12/8)
1.5

Remarque. Dans un calcul, un int sera considéré comme un float au besoin.

  • L’opérateur ** calcule des puissances réelles.
print(2**3)
8
  • Les opérateurs // et % calculent respectivement le quotient et le reste de la division entière (voir Série 1).

Opérations sur les str

  • + effectue la concaténation de deux str.
print('Appelez-moi' + ' ' + 'Ishmael')
Appelez-moi Ishmael
  • str * int permet la concaténation d’un str avec lui même.
print('ha' * 3)
hahaha
  • On peut, bien sûr, combiner ces deux opérations.
print('under my umbrella' + ' ella' * 2 + ' eh' * 3)
under my umbrella ella ella eh eh eh

Données d’entrée et de sortie

Regardons à nouveau la structure d’un programme Python.

def fonction(x):
    [...]
    return y

fonction(input_data)

Comme mentionné précédemment, une fonction implémente une fonctionnalité qui peut généralement être appelée plusieurs fois sur différentes données (input_data). En pratique, input_data sera une collection d’objets, chacun ayant un certain type.

La fonctionnalité implémentée par une fonction consiste en la réalisation de certaines tâches qui peuvent (ou non) aboutir à la création d’un nouvel objet que la fonction renvoie (return).

La fonction print

La fonction print permet au programme d’afficher des informations sur l’écran (ou d’écrire des informations à d’autres endroits). Elle fonctionne ainsi :

  • Son input est un objet (elle accepte plusieurs types différents),
  • elle affiche ensuite un str associée à cet objet dans la sortie standard (sur l’écran),
  • elle retourne l’objet None.

Remarque. Bien que print retourne un objet, ce qu’elle retourne n’est pas la partie intéressante de sa sortie. On ne devrait donc jamais retourner un print.

def aire_triangle(base,hauteur):
    return print('L\'aire vaut', base * hauteur / 2)  

aire_triangle(9,3)
L'aire vaut 13.5
def aire_triangle(base,hauteur):
    return print('L\'aire vaut', base * hauteur / 2)  

aire_triangle(9,3)*2
L'aire vaut 13.5
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[22], line 4
      1 def aire_triangle(base,hauteur):
      2     return print('L\'aire vaut', base * hauteur / 2)  
----> 4 aire_triangle(9,3)*2

TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'

Utilisation de la fonction print

L’avantage du fait que print retourne un objet None est que cela n’a aucun effet sur le programme, c’est invisible pour la machine. Donc cela peut être utilisé pour donner des informations à l’utilisateur sans interférer.

def aire_triangle(base,hauteur):
    print('Je calcule l\'aire d\'un triangle...')
    return base * hauteur / 2  

print('A1 vaut', aire_triangle(9,3))
print('A2 vaut', aire_triangle(1,5))
Je calcule l'aire d'un triangle...
A1 vaut 13.5
Je calcule l'aire d'un triangle...
A2 vaut 2.5

Remarque. Donner plusieurs entrées à print lui fait imprimer la concaténation de toutes les str correspondantes.

Caractères d’échappement

Les caractères d’échappement permettent d’inclure des caractères spéciaux dans les chaînes de caractères. Ils commencent par un backslash \

  • \n : nouvelle ligne
  • \t : tabulation
  • \\ : backslash littéral
  • \' : guillemet simple (apostrophe)
  • \" : guillemet double
print('Première ligne\nDeuxième ligne')
print('Nom:\tJean\nÂge:\t25')
print('Il a dit : \'Bonjour !\'')
print("Il a dit : 'Bonjour !'")
Première ligne
Deuxième ligne
Nom:    Jean
Âge:    25
Il a dit : 'Bonjour !'
Il a dit : 'Bonjour !'

Nouvelles lignes avec print

Par défaut, print ajoute automatiquement une nouvelle ligne à la fin. On peut modifier ce comportement avec le paramètre end :

###
print('Première ligne')
print('Deuxième ligne')
print('---')
print('Sans nouvelle ligne', end=' ')
print('suite sur la même ligne')
print('Avec un point', end='.')
print(' Fin')
Première ligne
Deuxième ligne
---
Sans nouvelle ligne suite sur la même ligne
Avec un point. Fin

Remarque. Le paramètre end='' permet de ne rien ajouter à la fin.

Séparateurs personnalisés

Le paramètre sep contrôle comment les différents arguments de print sont séparés. Par défaut, c’est un espace :

###
print('a', 'b', 'c')  # séparateur par défaut
print('a', 'b', 'c', sep='-')
print('a', 'b', 'c', sep=' | ')
print('Bond', 'James Bond', sep=', ')
print(2024, 12, 25, sep='/')
a b c
a-b-c
a | b | c
Bond, James Bond
2024/12/25

Casting

Le casting nous permet de convertir un objet d’un certain type en un objet d’un autre type (en fait, un nouvel objet est créé).

  • Les fonctions int et float peuvent créer un objet du type correspondant à partir d’un autre type numérique ou même d’un str.

    print(int(2.7), ';', float(4))
    2 ; 4.0
  • Similairement, la fonction str peut crée un objet de type str à partir d’un objet de type numérique.

    print (2 * str (1.2) )
    1.21.2

Illustration du casting avec print

En fait, nous avons déjà vu un exemple de casting : rappelez-vous que print peut accepter plusieurs types comme arguments.

print('Ocean\'s', 5*5-7-7)
Ocean's 11

Cela fonctionne parce que print appelle en interne str sur tout argument qui lui est passé. Donc si un objet peut être casté en str, print peut l’afficher !

Stocker les objets en mémoire

  • Dans une fonction, on a souvent besoin de stocker un objet pour le réutiliser plus loin. Pour ce faire, on utilisera des variables.
  • Une variable est une référence à un objet en mémoire, un nom par lequel on le désigne.
  • Une variable est créée par une instruction d’affectation, de la forme
variable = expression
x = 12+4
print(x)
16
x = bytes([*range(115,121)]).decode()[:5]
print(x)
stuvw
x = 7
print(x)
x='Sept'
print(x)
7
Sept
  • Lorsqu’une expression fait intervenir une variable, l’objet référencé par la variable est utilisé pour évaluer l’expression.

Remarque. On peut affecter une variable, puis la réaffecter après.

variable = expression

x = 23
print(x, type(x))
23 <class 'int'>
x = bytes([*range(115,121)]).decode()[:5]
print(x, type(x))
stuvw <class 'str'>

Il vaut la peine d’examiner en détail ce qui se passe ici:

  • Python évalue d’abord expression.
  • Ceci aboutit à un certain objet, que Python stocke en mémoire (s’il n’est pas déjà stocké).
  • Finalement, Python associe variable comme référence pour désigner cet objet.

Remarque. Cela diffère du fonctionnement de l’égalité en mathématiques. En particulier, ce n’est pas symétrique !

Création de plusieurs variables avec la même expression

Que devrait-il se passer dans ce cas ?

from fonctions_ics import fonctionA
x = fonctionA(2)
y = fonctionA(2)
print(id(x),id(y))

La dernière ligne print(id(x),id(y)) sert à vérifier si x et y correspondent au même objet. Est-ce le cas ? La réponse est… on ne peut pas le dire de manière générale ! Cela dépend de expression. Crée-t-elle un nouvel objet ou en trouvera-t-elle un pré-existant en mémoire ?

from fonctions_ics import fonctionA
x = fonctionA(2)
y = fonctionA(2)
print(id(x),';',id(y))

13965904 ; 14390408

from fonctions_ics import fonctionB
x = fonctionB(2)
y = fonctionB(2)
print(id(x), ';', id(y))

2788132 ; 2788132

Remarque. La plupart des fonctions en Python renvoient un nouvel objet. Ainsi, le plus souvent, les variables créées avec la même expression ne feront pas référence au même objet.

Création d’un nouvel objet

Les listes illustrent bien la remaque précédente : le simple fait d’écrire une liste amène Python à créer un nouvel objet.

x = [1,2,3]
y = [1,2,3]
print(x, ';', y, '\n', id(x), ';', id(y))

[1,2,3] ; [1,2,3]
14786928 ; 14390408

  • À la 1ière ligne, Python évalue l’expression [1,2,3], ce qui crée un objet qui est associé à la variable x (il s’agit d’un objet de type list, nous y reviendrons dans le cours 4).
  • À la 2ième ligne, Python re-évalue l’expression [1,2,3], ce qui crée un nouvel objet (avec la même valeur que l’objet référencé par x) qui est associé à la variable y.

var2 = var1

x = [1,2,3]
y = x
print(x, y, '\n', id(x), id(y))
[1, 2, 3] [1, 2, 3] 
 127110739461440 127110739461440

En suivant ce que nous venons d’apprendre, voyons ce qui devrait se passer à la 2ième ligne :

  • Python évalue l’expression x, cette expression renvoie un objet spécifique (en particulier, elle n’en crée pas).
  • Python implémente ensuite y comme référence à cet objet.

Remarque. En résumé, ce code attribue deux noms à l’objet initial : x et y. Il vaut mieux éviter ce type de commandes, car avoir plusieurs variables pour un même objet peut prêter à confusion.

Affectation avec opération

Commençons par un exemple d’affectation simple avec une expression :

x = 3
y = x + 1
print(y)
4

Affectation avec opération

Mais, si x est déjà une variable, il n’y a aucune raison pour que nous ne puissions pas la réaffecter à une expression qui contient x !

x = 3
x = x + 1
print(x)
4

Pour écrire cela de manière plus concise, on utilise x += 1.

Remarque. Python propose d’autres affectations avec opération :

  • x *= 3 (multiplication),
  • x -= 5 (soustraction),
  • x /= 2 (division),
  • x %= 3 (modulo), etc.

Tous fonctionnent selon le même principe :

x op= y est équivalente à x = x op y.

Affectation multiple

Python permet d’affecter plusieurs variables en une seule ligne :

x, y = 3, 4
print(x, y)
3 4

Cette opération fonctionne ainsi :

  • Python évalue d’abord toutes les expressions à droite du = (ici 3 et 4).
  • Puis il affecte simultanément ces objets aux variables de gauche dans le même ordre.

Remarque. Le nombre de variables à gauche doit correspondre au nombre d’expressions à droite, sinon Python génère une erreur.

Choisir le nom d’une variable

On peut nommer une variable comme on veut, à condition…

  • …de ne pas utiliser un des mots-clés réservés du langage Python (pour une liste complète voir ici).
  • …que le nom de variable contienne uniquement des lettres, chiffres, et underscore, et commence par une lettre ou un underscore.
  • …de se rappeler que les noms de variable en Python sont sensibles à la casse.

Remarque. Même si on peut nommer une variable comme on veut, il vaut mieux utiliser des noms parlants, et suivre les conventions de nommage de variables.

Les fonctions dans Python

Regardons à nouveau la structure d’un programme Python.

def fonction(x):
    [...]
    return y

fonction(input_data)

Pour l’instant, nous nous sommes concentrés sur la dernière partie, en discutant le input_data. Notre objectif est maintenant d’étudier la première partie : les fonctions, comment les comprendre et les définir.

Les fonctions dans Python

En informatique, une fonction est un ensemble d’instructions qui accomplit une tâche spécifique. Elle prend en entrée du input data (ou non) et retourne du output data (ou non).

Grâce aux fonctions, on peut :

  • effectuer plusieurs fois la même tâche au sein d’un programme mais avec des valeurs différentes, et simplifier sa mise à jour,
  • rendre le code plus lisible et cacher certaines instructions superflues à l’utilisateur et d’autres programmeurs,
  • partager du code entre plusieurs développeurs. En effet, le plus souvent, on importe une fonction depuis une bibliothèque plutôt que d’en écrire une nouvelle.

Définir une fonction

  • On déclare une fonction par l’instruction def suivie par le nom de la fonction puis des paramètres entre parenthèses (si nécessaire), et finalement de deux points,

  • Le corps de la fonction doit suivre: c’est un bloc d’instruction qui doit être indenté.

  • Le corps de la fonction peut contenir une ou plusieurs instructions return. Ces instructions terminent l’exécution de la fonction et retournent un objet.

def fonction(paramètre1, paramètre2):
    [corps de la fonction]
    return output

Remarque. Une fonction peut prendre zéro paramètres.

Appeler une fonction

  • Une fois qu’une fonction a été définie, nous pouvons l’appeler. L’appel doit toujours avoir lieu après la définition.

  • Syntaxe de l’appel de la fonction: le nom de la fonction suivi, entre parenthèses, d’un argument pour chaque paramètre: c’est l’objet effectif qui est lié à ce paramètre.

def vol_cylindre(r, h):
    v = 3.14 * r**2 * h
    return v

v1 = vol_cylindre(2, 5)
print(v1)

v2 = vol_cylindre(5, 2)
print(v2)
62.800000000000004
157.0

return

  • Dès qu’un return est exécuté, le programme quitte la fonction et l’appel de la fonction est évalué à l’objet retourné (s’il y en a un). On peut afficher cet objet, l’affecter à une variable, l’utiliser dans un calcul…

  • Si aucune instruction return n’est exécutée, la fonction retourne l’objet None (ce qui signifie qu’elle ne retourne aucune expression).

  • Si l’instruction return x, y est exécutée, le programme quitte le code et la fonction retourne les deux objets x et y, qui peuvent être affectés simultanément à deux variables. De même, une fonction peut retourner n’importe quel nombre d’expressions.

Exemples de return

def f(x):
    print('Vous avez dit', x, '?')

answer = f(3)
print('f a retourné', answer)
Vous avez dit 3 ?
f a retourné None
def g(num):
    return num + 1

x = 3
print(x, 'augmenté de 1 vaut', g(x))
3 augmenté de 1 vaut 4
def carre(x):
    perimetre = 4 * x
    aire = x ** 2

    return perimetre, aire

p, a = carre(3)
print('le carré de côté 3 a périmètre', p, 'et aire', a)
le carré de côté 3 a périmètre 12 et aire 9

Exemple Final

Bravo. Vous êtes arrivé à la fin du premier cours ICS. Nous avons juste le temps de voir un dernier exemple qui résume tout ce que nous avons vu aujourd’hui.

Pour illustrer tout ce que nous avons vu aujourd’hui, nous allons exécuter le code dans un débogueur qui nous permet de le parcourir ligne par ligne.

def aire_disque(r):
    print("\tje vais calculer l'aire du disque de rayon", r)
    aire = 3.14 * r**2
    print("\tj'ai calculé l'aire du disque de rayon", r, ", je retourne la valeur", aire)
    return aire

def vol_cylindre(r, h):
    print("je vais calculer le volume du cylindre de rayon", r, "et hauteur", h)
    v = h * aire_disque(r)
    print("j'ai calculé le volume du cylindre de rayon", r, "et hauteur", h, ", je retourne", v)
    return v

def surface_cylindre(r, h):
    print("je vais calculer la surface du cylindre de rayon", r, "et hauteur", h)
    s = 2 * 3.14 * r * h + 2 * aire_disque(r)
    print("j'ai calculé la surface du cylindre de rayon", r, "et hauteur", h, ", je retourne", s)
    return s

rayon_base = 3
hauteur = 5

volume = vol_cylindre(rayon_base, hauteur)
print("le cylindre de rayon", rayon_base, "et hauteur", hauteur, "a volume", volume)

print("\n****\n")

surface = surface_cylindre(rayon_base + 3, hauteur - 2)
print("le cylindre de rayon", rayon_base + 3, "et hauteur", hauteur - 2, "a surface", surface)

Cliquez ici pour accéder au débogueur, où vous pouvez copier le code ci-dessus.