Informatique et Calcul Scientifique

Cours 2

Author

Leonard Hardiman et Ghid Maatouk

Docstrings

La docstring d’une fonction est un commentaire qui apparaît dans la définition de la fonction, encadré par trois guillemets . Il peut s’étaler sur plusieurs lignes.

def puissance_reelle(x, y):
    '''
    input: deux nombres réels x et y
    output: x^y
    Attention: si y est un float ET x et négatif, le résultat ne fait aucun sens
    '''
    return x ** y
from fonctions_ics import puissance_reelle

help(puissance_reelle)

Help on function puissance_reelle in module fonctions_ics:

puissance_reelle(x, y)
  input: deux nombres réels x et y
  output: x^y
  Attention: si y est un float ET x et négatif, le résultat ne fait aucun sens

Remarque. Pour voir la docstring d’une fonction, exécutez la fonction built-in help sur celle-là.

Paramètres optionnels

  • Il est possible de spécifier une valeur par défaut pour certains paramètres qui sont donc optionnels.

  • Les paramètres optionnels doivent être spécifiés après les paramètres obligatoires dans la définition de la fonction.

  • Lors de l’appel de la fonction, si aucun argument n’est fourni pour un paramètre optionnel, la valeur par défaut pour ce paramètre est utilisée.

def saluer(nom = 'toi'):
    print('salut', nom, '!')

saluer('Sam')
saluer()
salut Sam !
salut toi !
def laBise(pers1, pers2, n = 3):
    print(pers1, 'et', pers2, 'se font la bise!')
    print('mwah ' * n)

laBise('Bernard', 'Bianca')
laBise('Bernard', 'Bianca', 2)
Bernard et Bianca se font la bise!
mwah mwah mwah 
Bernard et Bianca se font la bise!
mwah mwah 

Paramètres optionnels : la fonction print

  • Nous avons déjà vu que la fonction print admet deux paramètres optionnels: end et sep.

  • Si une valeur est fournie pour le paramètre sep dans l’appel à la fonction print, cette valeur sert de séparateur entre les arguments passés à print, sinon la valeur par défaut (espace) est prise (de même pour end).

    print('chou', 'fleur')
    print('chou', 'fleur', end = '!')
    print('chou', 'fleur', sep = '-')
    print('chou', 'fleur', sep = '-', end = ':)')
    chou fleur
    chou fleur!chou-fleur
    chou-fleur:)

Remarque. La fonction print est un exemple assez spécial car il faut explicitement nommer end et sep dans l’appel.

Portée de variables

  • Lorsqu’une variable est créée dans un programme Python, les portions du programme qui peuvent y accéder sont la portée de cette variable.

  • La portée d’une variable dépend de l’endroit du code où elle est définie.

  • Une variable définie dans une fonction est dite locale (à cette fonction).

  • Une variable définie à l’extérieur de toute fonction est dite globale.

  • Les règles pour déterminer la portée d’une variable sont les suivantes.

Portée de variables : Règle I

Une variable locale à une fonction n’est pas accessible hors de cette fonction.

def fonction():
    v = 3
    print("Dans la fonction, v vaut", v)
    
fonction()
print("À l'extérieur de la fonction, v vaut", v)
Dans la fonction, v vaut 3
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 6
      3     print("Dans la fonction, v vaut", v)
      5 fonction()
----> 6 print("À l'extérieur de la fonction, v vaut", v)

NameError: name 'v' is not defined

Portée de variables : Règle II

Une variable globale est accessible à l’intérieur d’une fonction.

def fonction():
    print("Dans la fonction, v vaut", v)
    
v = 43
fonction()
Dans la fonction, v vaut 43

Attention. Si on essaye de réaffecter une variable globale dans une fonction, Python va créer une variable locale qui a le même nom.

def fonction():
    v = 2
    print("Dans la fonction : v vaut", v)
    
v = 43
print("Avant l'appel, v vaut", v)
fonction()
print("Après l'appel, v vaut", v)
Avant l'appel, v vaut 43
Dans la fonction : v vaut 2
Après l'appel, v vaut 43

L’analogie des “registres”

On peut imaginer que chaque fonction garde un registre des variables qui sont définies dans la portée de cette fonction.

  • Lorsqu’une expression fait intervenir une variable x, l’interpréteur Python va d’abord aller vérifier si cette variable est présente dans le registre local de cette fonction.
  • S’il ne la trouve pas, il ira regarder dans le registre global et prendra l’objet référencé par x dans ce registre.
  • S’il ne la trouve pas non plus dans le registre global, il déclare une erreur.

Remarque. On peut afficher ces registres local et global avec les fonctions locals et globals.

global

Si, dans une fonction, on veut pouvoir modifier une variable définie globalement, il faut la déclarer comme globale dans la fonction, avant de l’affecter.

Ceci indiquera à l’interpréteur Python de ne pas enregistrer cette variable dans le registre local à la fonction, mais de toujours consulter le registre global pour accéder à l’objet référencé par cette variable.

def fonction():
    global v
    v = 5
    print("dans f, v vaut", v)
    
v = 3
print("avant f, v vaut", v)
fonction()
print("après f, v vaut", v)
avant f, v vaut 3
dans f, v vaut 5
après f, v vaut 5

Modularité

Les fonctions doivent être définies à un endroit accessible par Python. Elles peuvent être :

  • définies par l’utilisateur dans le fichier code exécuté.
  • built-in : définies dans la distribution Python (print(...), max(...), abs(...)).
  • définies dans un autre fichier accessible (ex: fonctions_ics.py).
  • définies dans une bibliothèque standard Python, à importer (ex: random.randint(), math.sqrt()).
  • créées par d’autres utilisateurs, à installer puis importer.

Import de modules

Il existe différentes manières d’importer une fonction d’un module :

Méthode d’import Appel
from module_name import fonction fonction()
from module_name import * fonction()
import module_name module_name.fonction()
import module_name as alias alias.fonction()
from math import sqrt
x = 9
print('La racine carree de', x, 'vaut', sqrt(x))
La racine carree de 9 vaut 3.0
from random import *
print('Voici un nombre aleatoire :', randint(0, 10))
Voici un nombre aleatoire : 7
import math
print('cos(pi) =', math.cos(math.pi))
cos(pi) = -1.0
import math as m
print('cos(pi) =', m.cos(m.pi))
cos(pi) = -1.0
from fonctions_ics import fonctionDeSam

fonctionDeSam('La fondue')

La fondue est mieux au Valais!

Remarque. La deuxième méthode est la moins recommandée car elle risque d’effacer des fonctions locales.

Modules courants

Voici quelques modules classiques :

Module Description
math Fonctions mathématiques (sin, sqrt, pi)
random Génération de nombres aléatoires
time Fonctions liées au temps
os Interaction avec le système d’exploitation
sys Interaction avec l’interpréteur Python

Remarque. Liste complète : docs.python.org/3/py-modindex.html

Question motivante

Écrivons un programme qui génère deux points aléatoires sur cette grille de coordonnées entières et calcule la distance entre ces deux points.

import math
import random

def distance(x1, y1, x2, y2):
    "Retourne la distance entre deux points"
    return math.sqrt((x2-x1)**2 + (y2-y1)**2)

# Générer deux points aléatoires
x1, y1 = random.randint(0, 8), random.randint(0, 4)
x2, y2 = random.randint(0, 8), random.randint(0, 4)

# Afficher les 2 points et leurs distance
print('Point 1: (', x1, ', ', y1, ')', sep='')
print('Point 2: (', x2, ', ', y2, ')', sep='')
dist = distance(x1, y1, x2, y2)
print('Distance: ', dist)
Point 1: (1, 4)
Point 2: (6, 2)
Distance:  5.385164807134504

Flux de contrôle

  • Jusqu’ici, on ne sait écrire qu’un programme linéaire : une suite d’instructions qui est exécutée ligne après ligne, quelle que soit l’entrée du programme.

  • Pour écrire des programmes plus intéressants, on utilisera des instructions qui permettent à un programme d’exécuter une séquence différente d’instructions selon qu’une certaine condition est vérifiée ou pas.

  • On parle alors de structures conditionnelles.

L’instruction if

L’instruction if permet de d’évaluer une condition, puis d’effectuer un bloc d’instructions si la condition est évalue à True.

def commentaire_age(age):
    comm = str(age) + ' est un bel âge.'

    if age < 18:
        to_maj = 18 - age
        comm += ' ' + str(to_maj) + ' an(s) avant la majorité.'

    return comm

print(commentaire_age(23))
23 est un bel âge.

Remarque. Comme pour les définitions de fonction, le bloc de code relatif à une condition if est indiqué par une indentation. N’oubliez pas les deux-points après la condition.

L’instruction else

L’instruction else suit une instruction if (facultativement) et permet d’exécuter un bloc alternatif lorsque la condition du if est évaluée à False.

def commentaire_age(age):
    comm = str(age) + ' est un bel âge.'

    if age < 18:
        to_mat = 18-age
        comm += ' '+str(to_mat)+' an(s) avant la majorité'
    else:
        comm += ' Vous êtes majeur(e) !'

    return comm

print(commentaire_age(16))
16 est un bel âge. 2 an(s) avant la majorité

Remarque. Le bloc else est également commencé par deux-points et utilise une indentation.

L’instruction elif

L’instruction elif (contraction de «else if») permet de tester plusieurs conditions successives après un if initial. Elle s’exécute uniquement si les conditions précédentes sont évaluées à False.

def commentaire_age(age):
    comm = str(age) + ' est un bel âge.'

    if age < 18:
        to_mat = 18-age
        comm += ' '+str(to_mat)+' an(s) avant la majorité'
    elif age < 65:
        comm += ' Vous êtes majeur(e) et en âge de travailler !'
    else:
        comm += ' Profitez bien de votre retraite !'

    return comm

print(commentaire_age(35))
35 est un bel âge. Vous êtes majeur(e) et en âge de travailler !

Remarque. On peut utiliser plusieurs elif dans une même structure conditionnelle.

Les conditions booléennes

Mais qu’entendons-nous exactement par « une condition » ?

  • Rappelons que nous avons précédemment vu un objet True qui avait le type bool. Il n’existe qu’un seul autre objet de ce type, False.
  • Une condition booléenne est une expression qui évalue à l’un de ces objets.
  • Pour créer une condition booléenne, on peut utiliser des relations :
    • Pour comparer les valeurs de deux objets : <, <=, >, >=.
    • Pour tester l’égalité des valeurs de deux objets : ==, !=.
    x=4
    print(x<=3)
    False
    print(type(4) == type(7))
    print(type(4) == float)
    print(type(4) == int)
    True
    False
    True

Comparaisons de str

Les opérateurs de comparaison fonctionnent aussi avec les str :

  • L’égalité (==) est sensible à la casse.

    print('hello' == 'hello')
    print('hello' != 'world')
    print('hello' == 'Hello')
    True
    True
    False
  • L’ordre suit l’ordre lexicographique (comme dans un dictionnaire).

    print('a' < 'b') # Ordre lexicographique (alphabétique)
    print('zebre' < 'escargot')
    True
    False
  • Les majuscules ont une valeur plus petite que les minuscules.

    print('Banane' < 'abricot')  # Attention aux majuscules!
    print('eBay' < 'eau')
    True
    True
  • Un str est un préfixe de l’autre, la plus courte est plus petite.

    print('abc' < 'abcd')  # Plus court vient avant
    print('z' > 'abc')     # Premier caractère compte
    True
    True
  • Les espaces sont plus petits que toutes les lettres.

Des conditions non-booléennes

Dans une instruction if, si on donne une condition qui évalue à un objet qui n’est pas de type bool, Python essaye de caster cet objet en un bool.

  • Le nombre 0 (float ou int) est casté en False et tout autre nombre en True.
  • Un str vide est casté à False et tout autre str à True
  • En général, si un objet a une valeur « vide », il sera casté en False, sinon en True.
def parité(x) :
    if x % 2 :
        return 'impair'
    else :
        return 'pair' 

print(parité(3))
impair

Combiner des expressions booléennes

On peut combiner les valeurs de plusieurs expressions booléennes avec les opérateurs and, or et not.

def parité_et_signe(x):
    if x % 2 and x > 0:
        return 'impair et positif'
    if x % 2 and x < 0:
        return 'impair et négatif'
    if x > 0:
        return 'pair et positif'
    if x < 0:
        return 'pair et négatif'
    return 'pair, positif et négatif'

print(parité_et_signe(3))
impair et positif

Le fonctionnement de ces combinaisons est intuitif, mais nous le précisons ici à titre de référence.

A B A and B
True True True
True False False
False True False
False False False


A B A or B
True True True
True False True
False True True
False False False


A not A
True False
False True

Remarques finales sur if/else/elif

  • Un bloc de code vide après if/else/elif est illégal. Utilisez l’instruction pass.

    def somme(x,y):
        if x % 2 and x > 0:
            pass
        return x+y
    
    somme(3,5)
    8
  • On peut utiliser un if n’importe où dans notre code, y compris à l’intérieur d’un if ou else précédent.

  • Si le bloc de code que vous souhaitez après un if/else/elif ne prend qu’une seule ligne, vous pouvez éviter l’indentation et simplement l’écrire sur la même ligne.

    if 3 < 4 : print('Trois est inférieur à quatre')
    else : print('Waouh, quelle surprise !')
    Trois est inférieur à quatre