Le concept de fonction¶

Éviter les répétitions¶

Un premier exemple¶

In [ ]:
print("Cours : Informatique")
print("Mercredi : classe entière")
print("Horaires : 13H - 14H")
print()
print()
print()
print("TD : Informatique")
print("Lundi : groupes Kholles")
print("Horaires : 13H - 17H")

Comment simplifier le programme ?

In [ ]:
def sauterTroisLignes():
    print
    print
    print
In [ ]:
print("Cours : Informatique")
print("Mercredi : classe entière")
print("Horaires : 13H - 14H")

sauterTroisLignes()

print("TD : Informatique")
print("Lundi : groupes Kholles")
print("Horaires : 13H - 17H")

Les arguments¶

In [ ]:
def afficherCours(matiere, effectif, jour, horaire):
    print("Cours : " + matiere)
    print(jour + " : " + effectif)
    print("Horaires : " + horaire)
In [ ]:
afficherCours("Informatique", "Classe", "Mercredi", "13H - 14H")
sauterTroisLignes()
afficherCours("Informatique", "groupes:3, 7, 4, 5", "Lundi", "13H - 15H")

Question : Écrire une fonction permettant de sauter n lignes ?

In [ ]:
def sauter_n_lignes():
    
    
    
    
    
    
    
    

En résumé¶

Écrire une fonction c'est :¶

Un prototype¶

Le prototype d’une fonction est une forme générique qui dit comment on doit l’utiliser, mais qui ne dit pas comment elle a été codée. La syntaxe est la suivante:

$$\text{def name(arg1, arg2, ..., argN)}$$

arg1,arg2... sont les paramètres formels

Une définition¶

Une fonction est définie de la manière suivante :

  • un mot clé def suivi du nom de la fonction lui même suivi de parenthèses et terminé par le caractère :
  • Entre les parenthèses il peut y avoir des paramètres, utilisés comme variables locales
  • Le corps de la fonction est indentée d'un niveau (bloc indenté)
  • La fonction peut renvoyer un ou des objets à l'aide du mot clé return

Utiliser une fonction c'est :¶

Appeler la fonction avec son nom suivi des parenthèses avec le ou les paramètres effectifs encore appelés arguments

  • les parenthèses y sont toujours, même lorsqu’il n’y a pas de paramètre
  • par défaut paramètres effectifs et paramètres formels doivent coïncider en nombre (et en type)

Renvoyer une valeur¶

Pour l'instant nous nous sommes contentés d'afficher des objets de type String, mais il est possible de renvoyer des objets de type : int, float, ..., avec le mot clef return

In [ ]:
import math
def hypothenus(x, y):
    return math.sqrt(x**2 + y**2)
In [ ]:
valeur_hypothenus = hypothenus(3, 4)

Plusieurs return¶

In [ ]:
def maxi2(a, b):
    '''
    Renvoie le plus grand des deux arguments
    a, b de type int, float
    '''
    if a >= b:
        return a
    else:
        return b

Remarque : On utilise les tripes guillemets ou triples quotes pour indiquer une chaîne multi−lignes qui servira de documentation pour la fonction, cette documentation est indispensable

In [ ]:
help(maxi2)

La nécessité de structurer le code¶

Quand on écrit un programme il est absolument nécessaire de rendre son code lisible et modulable

Bien rédiger¶

  • Commenter le code
  • Style typographique uniforme
  • Indentation uniforme
  • éviter d’avoir un morceau de code en plusieurs exemplaires

Structurer¶

  • Créer des fonctions
  • Décrire les prototypes des fonctions
  • Tester les fonctions individuellement
  • Tester les communications entre les fonctions

Variables globales¶

In [ ]:
x = 3
def reset():
    x = 0

Question : Quelles sont les valeurs de x affichées par ce programme ?

In [ ]:
print("Avant reset : ")
print(x)
reset()
print("Apres reset :")
print(x)

Variables locales¶

In [ ]:
y = "variable globale"
def message():
    y = "variable locale"
    return y

Question : Quelles sont les valeurs de y affichées par ce programme ?

In [ ]:
print(y)
print(message())
print(y)

Variable globale cachée par une variable locale¶

L'utilisation de variables globales portant le même nom que des variables locales peut engendrer des problèmes de transmission d'informations.

In [ ]:
y = "variable globale"
def message(y):
    y = "Variable locale"
    y += y
    return y
In [ ]:
print(y)
print(message(y))
print(y)

Appel de fonction dans une fonction¶

Directement dans le code d'une des fonctions¶

In [ ]:
def maxi3(a, b, c):
    '''
    Renvoie le plus grand des trois arguments
    a, b, c de type int, float  
    '''
    return maxi2(maxi2(a,b),c)
In [ ]:
maxi3(12, 7.7, 23)

Remarque¶

Le typage dynamique des arguments avec python permet d'utiliser une même fonction dans différents contextes. Cependant il est recommandé de commenter le type attendu pour chacun des arguments dans la documentation de la fonction

In [ ]:
maxi3("mat", "mal", "mais")

Par l'intermédiaire des paramètres¶

In [ ]:
def f(x):
    return 2*x + 3

def g(x):
    return -3*x + 2

def f_rond_g(f1, f2, x):
    return f1(f2(x))
In [ ]:
print(f_rond_g(f, g, 3))

Accepter un nombre indéterminé d'arguments : liste d'arguments non nommés¶

Il est possible de spécifier à python que le nombre d'arguments de la fonction n'est pas connu à l'avance en précédant le nom d'argument par un $*$
Dans ce cas la séquence d'arguments est par défaut de type tuple

In [ ]:
def multiple(multiple, *sequence):
    for val in sequence:
        if val%multiple == 0:
            print val, "est multiple de", multiple
        
multiple(5, 10, 23, 25)

Retour sur la portée des variables dans une fonction¶

Espace de nom¶

Python utilise ce que l’on appelle des espaces de noms pour suivre les variables. Un espace de noms est semblable a un dictionnaire dans lequel les clés sont les noms des variables et les valeurs du dictionnaire sont les valeurs des variables.

A n’importe quel point dans un programme Python, il y a plusieurs espaces de noms disponibles. Chaque fonction a son propre espace de noms, appelé espace de noms local, pour :

  • les variables de la fonction définies localement
  • les arguments.

Exemple (les images ont été réalisées à partir du site http://pythontutor.com/)

Chaque module a son propre espace de noms, appelé l’espace de noms global pour ses:

  • variables,
  • fonctions,
  • ses modules importés
  • classes.

Les instructions exécutées dans un shell Python sont considérées comme faisant partie d'un module appelé __main__. Les primitives Python appartiennent à un module appelé __builtins__

Fonction et liaison dynamique¶

Seule la dernière définition d'une fonction est prise en compte

In [ ]:
# -*- coding: utf-8 -*-
def action(a):
    print a + u" fait la sieste"

def snoopy():
    action("Snoopy")
snoopy()

def action(a):
    print a + u" est réveillé"
snoopy()

Passage des arguments¶

Les types mutables et non mutables¶

non-mutable, toute tentative de modification conduit à la création d’un nouvel objet (identifiant différent) portant le même nom et ayant la nouvelle valeur: int, float, string, tuple ...

mutable, c’est à dire qu’ils peuvent être modifiés sans que l’identificateur de l’objet ne change: list, dictionnaire...

Passage par valeur ou référence¶

Tout argument non-mutable d'une fonction va se comporter comme une variable locale, dans ce cas il ne peut pas être réaffecté par la fonction, on dit que l’argument est passé par valeur

In [ ]:
def neModifiePas(x):
    x = 2
    return x
x = 3
neModifiePas(x)
print x

Tout argument mutable d'une fonction peut-être modifier par cette fonction, dans ce cas on dit que l’argument est passé par référence (notion de pointeur). Ce qui a pour effet de modifier également la ou les valeurs de cet objet en dehors de la fonction si il existe.

In [ ]:
def swapLastFirst(x):
    x[0], x[-1] = x[-1], x[0]
    return x
x = [0, 1, 2, 3, 4]
swapLastFirst(x)
print x

Lambda fonctions¶

Syntaxe condensée de la definition d’une fonction. La particularite d’une lambda fonction est de pouvoir être anonyme, c’est à dire pas qu’il n’y a pas besoin de nommer la fonction (pas de mot clef def)

Avec un nom de variable¶

In [ ]:
from math import log10
pH = lambda x : -log10(x)
print "pH =", pH(1e-3)

De manière anonyme¶

In [ ]:
print "pH =", (lambda x : -log10(x))(1e-3)

Un premier exemple est l’utilisation de la fonction map, qui permet d’appeler une fonction sur chacun des éléments d’une liste. Définir une fonction localement peut-être très utile quand on sait que cette fonction sera utilisée exclusivement à cet endroit du code. Cela peut nous éviter de devoir la déclarer, lui trouver un nom, et s'assurer qu’il est unique dans la totalité de notre code afin d'éviter les éventuels problèmes de redéfinitions de fonctions (exemple de liaison dynamique catastrophique)

In [ ]:
a = [1, 2, 3, 4]
map(lambda x : x**2, a)

Un autre exemple pour la forme

In [ ]:
print map(lambda mot: len(mot), "C'est bientôt les vacances".split())

Enigme¶

Écrire une fonction qui vérifie la bon fonctionnement de maxi3.

In [ ]:
#une fonction maxi2 fausse
def maxi2(a, b):
    if a >= b:
        return b
    else:
        return a
In [ ]:
def maxi3(a, b, c):
    return maxi2(maxi2(a, b), c)
In [ ]:
def correctMaxi3(f):
    global maxi2
    tmp = maxi2
    print id(tmp)
    if f(2,4,5)==5 and f(3,7,4)==7 and f(8,2,4)==8 and f(5,3,2)==5 and f(2,4,1)==4:
        return "OK"
    else:
        def maxi2c(a, b):
            if a >= b:
                return a
            else:
                return b
        maxi2 = maxi2c
        print id(maxi2)
        if f(2,4,5)==5 and f(3,7,4)==7 and f(8,2,4)==8 and f(5,3,2)==5 and f(2,4,1)==4:
            maxi2 = tmp
            print id(maxi2)
            return "probleme avec maxi2"
        else:
            return "Probleme dans maxi3"
In [ ]:
correctMaxi3(maxi3)
In [ ]:
import time
print time.strftime("Version du "+'%d/%m/%y %H:%M',time.localtime())

Christophe Casseau mail : isncaju@gmail.com