Nov 21, 2024

Wiki

Python

Aide

edit SideBar

Search

Les fonctions


Comment définir une fonction

La définition d'une fonction

Pour définir une fonction dans un programme, c’est tout simple :

  >>> def nom_fonction(liste de parametres) :
  ...     instruction1
  ...     instruction2
  ...     etc.

Le décalage, comme toujours, est obligatoire, et la convention est d'utiliser des noms de fonction en lower_case : pas de majuscule, et des _ si nécessaire.

On peut utiliser le mot-clé return pour spécifier le retour de la fonction :

  >>> def incremente(x) :
  ...     y = x + 1
  ...     return y
  ...
  >>> incremente(5)
  6

Si aucun retour n'est précisé, alors le retour sera None.

Exemple d'une fonction récursive

On peut appeler récursivement une fonction. Illustrons cela par une fonction factorielle...

  >>> def facto(n) :
  ...     if n == 1 :
  ...         return 1
  ...     else :
  ...         return n*facto(n-1)
  ...
  >>> facto(10)

Version pythonique :

  >>> from itertools import accumulate
  >>> import operator
  >>> list(accumulate(range(1, 11), operator.mul))

Travaux pratiques

La formule suivante donne la relation entre une température exprimée en degrés Celsius $c$ et la même température exprimée en degrés Fahrenheit $f$.

$ f = 32 + 1,8 \times c $

Donner le code d'un programme python qui

  1. demande à l'utilisateur de saisir une température exprimée en degrés Celsius, puis qui affiche la température en degrés Farenheit;
  2. affiche en plus les messages
    • " à cette température l'eau gèle" si la température saisie est inférieure à 0°C;
    • " à cette température l'eau bout" si la température saisie est supérieure à 100°C.

Paramètres spéciaux

Pour une fonction, il existe des paramètres spéciaux capable d'intercepter un nombre indéfini de valeurs. La signature de la fonction devient polymorphique.

*args
placé à la fin de la liste des paramètres, cette variable agrège dans un tuple tous les paramètres qui n'ont pas été définis.
 >>> def toto(x, *args):
 ...     print(args)
 ... 
 >>> toto(5, 2, 'a')
 (2, 'a')
**kw
placé juste après *args, cette variable intercepte tous les paramètres nommés qui restent. Ils sont rangés dans un dictionnaire.
  >>> toto(5, 'a', r = 2)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: toto() got an unexpected keyword argument 'r'
  >>>
  >>> def toto(x, *args, **kw):
  ...     print(args)
  ...     print(kw)
  ... 
  >>> 
  >>> toto(5, 2, 'a')
  (2, 'a')
  {}
  >>> toto(5, 'a', r = 2)
  ('a',)
  {'r': 2}

Ces paramètres spéciaux sont à utiliser avec parcimonie.

Les generators

Présentation

Les generators, introduits dans la version 2.2, offrent une technique de programmation originale, qui permet à une fonction ou une méthode de renvoyer des résultats intermédiaires.

Le mot-clef return est remplacé par yield.

La première fois que ce mot est rencontré, l'exécution est stoppée et un objet de type generator est renvoyé, qui contient le contexte local de la fonction, et une méthode next

A chaque appel de cette méthode, la fonction est appelée jusqu'au prochain yield.

Un premier exemple

On définit une fonction avec trois yield (et aucun return). On va voir que

  • au premier appel, c'est le premier yield qui sera retourné,
  • au deuxième appel, ce sera le second,
  • etc.

Définissons notre generator :

  >>> def ca_compte():
  ...     yield 'Et de un'
  ...     yield 'Et de deux'
  ...     yield 'Et de trois'
  ... 
  >>> je_compte = ca_compte()

Premier appel...

  >>> next(je_compte)
  'Et de un'

Et ça continue ainsi, de yield en yield...

  >>> next(je_compte)
  'Et de deux'
  >>> next(je_compte)
  'Et de trois'

Enfin, une fois que tous les yield ont été appelés, une exception StopIteration est levée...

  >>> next(jeCompte)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  StopIteration

Utilisation dans une boucle

La fonction next pour passer à la prochaine valeur, et la levée de l'exception StopIteration, rendent les generators compatibles avec les boucles :

  >>> je_compte = ca_compte()
  >>> for element in je_compte:
  ...     print(element)
  ...
  Et de un
  Et de deux
  Et de trois

Exemple complet d'utilisation

Suite de Fibonacci

Voici à quoi peuvent servir les generators, en l'occurence à programmer proprement la suite de Fibonacci :

  >>> def fibonacci():
  ...     a, b = 0, 1
  ...     while True:
  ...         yield b
  ...         a, b = b, a+b
  ...
  >>> suite = fibonacci()
  >>> n = int(input('Combien de termes ? '))
  >>> for k in range(n):
  ...     print(next(suite), end=', ')
  ...
  1, 1, 2, 3, 5, 8, 13, 21, 34, 55

On a mis un yield dans une boucle while infinie : on pourra donc appeler autant de fois que l'on veut next(suite). Cette manière de procéder peut se réutiliser dans plein de contextes différents.

On aurait pu procéder à une version récursive, en prenant bien soin de ne pas recalculer les termes précédemment calculés, via lru_cache:

  from functools import lru_cache

  @lru_cache(maxsize=None)
  def fib_cache(n):
      if n<2: return n
      return fib_cache(n-1)+fib_cache(n-2)

Nombres premiers

Le generator suivant permet d'obtenir autant de nombres premiers que l'on veut :

  >>> def nombre_premier():
  ...     nombre = 2
  ...     premiers = [2]
  ...     yield nombre
  ...     while True:
  ...         nombre += 1
  ...         if len([k for k in premiers if nombre%k == 0]) == 0:
  ...             yield nombre
  ...             premiers.append(nombre)
  ... 
  >>> premier = nombre_premier()
  >>> next(premier)
  2
  >>> next(premier)
  3
  >>> next(premier)
  5
  >>> next(premier)
  7
  >>> next(premier)
  11

Les genexp

Les generators expressions (genexp) fournit une écriture concise pour les generators simples, en se basant sur les list comprehensions : les crochets de ces dernières sont remplacées par des parenthèses...

  >>> gen = ( k for k in range(10) if k%3 == 0)
  >>> next(gen)
  0
  >>> next(gen)
  3
  >>> next(gen)
  6
  >>> next(gen)
  9
  >>> next(gen)
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  StopIteration

Pour aller plus loin

Le module itertools offre des outils avancés pour travailler sur les generators.

Optimiser une fonction

Python étant un langage interprété, il ne connaît pas à l'avance le code qu'il va exécuter : à la différence d'autres langages, il n'y a pas d'optimisation des appels de fonctions fréquemment utilisées, par un appel à une adresse mémoire unique.

Pour contourner le problème, et gagner du temps à chaque nouvel appel d'une fonction donnée, on peut stocker cette dernière dans une variable locale, comme l'illustre le bout de code suivant :

  >>> from time import time
  >>> def toto(N):
  ...     x=float(N)/1000
  ...     x=1000*x
  ... 
  >>> def sans(N):
  ...     dd=time()
  ...     for k in range(N):
  ...         toto(k)
  ...     return time()-dd
  ... 
  >>> def avec(N):
  ...     tt=toto
  ...     dd=time()
  ...     for k in range(N):
  ...         tt(k)
  ...     return time()-dd
  ... 

Dans ce qui précède, la fonction sans appelle N fois la fonction toto, sans l'avoir stockée dans une variable au préalable, quand la fonction avec fait la même chose, en utilisant une variable locale pour toto. Regardons le nombre de fois où avec est plus rapide que sans...

  >>> cpt = 0
  >>> for k in range(1000):
  ...     if sans(10000) > avec(10000):
  ...          cpt+=1
  ... 
  >>> print(cpt/10)
  93.2

On trouve donc un gain de temps dans 93,2% des cas.

Travaux pratiques

Premiers exercices

  1. Ecrire une fonction qui renvoie True ou False, suivant que l'entier fournis en argument est pair ou impair.
  2. Ecrire une fonction qui renvoie True ou False, suivant que la chaîne de caractères fournie a une taille paire ou impaire.

Diviseurs d'un nombre

  • Faire une fonction qui renvoie la liste des diviseurs d'un entier passé en argument.
  • Faire une fonction qui teste si un nombre est premier. On pourra utiliser la fonction précédente.
  • Faire une fonction renvoyant la décomposition en nombres premiers d'un entier donné : $600 = 2^3 \times 3 \times 5^2$
  • Refaire les fonctions factorielle et Fibonacci, sans regarder la solution.

Page Actions

Recent Changes

Group & Page

Back Links