Python: Fonctions: Difference between revisions

From Wiki Cours
Jump to navigation Jump to search
(Created page with " === définir une fonction === #définition d'une fonction: deux options suivant la complexité <source lang="py"> def f(x): return x**2 f = lambda x: x**2 </source> =...")
 
 
(25 intermediate revisions by the same user not shown)
Line 1: Line 1:
__NOTOC__
[[Memento_Python|<--Sommaire]]
== Déclarer et appeler une fonction ==


Deux options suivant la complexité


=== définir une fonction ===
1. définition usuelle à l'aide du mot-clé '''def''' et ouverture d'un bloc. '''return''' permet que la fonction renvoie un résultat mais ce n'est pas obligatoire, la fonction peut simplement manipuler les données en entrée
<source lang="py">
def f(x):
    return x**2
</source>
2. on peut également définir une fonction '''lambda''' ou 'fonction anonyme' selon
<source lang="py">
f = lambda x: x**2
</source>
dans l'exemple ci-dessus, le caractère anonyme n'est pas très clair car on lui a affecté la variable f pour l'utiliser avec f(x) comme précédemment le caractère anonyme apparaît lorsqu'on passe ce type de fonction en argument.
 
On demande l'exécution d'une fonction en utilisation les parenthèses '''()''' et en passant ses arguments à l'intérieur des parenthèses. Une fonction peut ne peut avoir d'argument et ne rien renvoyer :
<source lang="py">
def f():
    print("Hello")
f()    # appel de la fonction
f      # affiche l'objet fonction
g = f  # la variable g fait référence à la fonction f
g()    # même résultat que l'appel de f
a = f() # la fonction f() est exécutée mais comme elle ne
print(a)# renvoie rien, a est de type None
</source>


#définition d'une fonction: deux options suivant la complexité
== Récursivité ==
 
Une fonction peut s'appeler elle-même
<source lang="py">
<source lang="py">
def f(x):
def f(x):
     return x**2
    if x < 10: return x*f(x+1)
    else: return 10
print(f(1), f(10), f(9), f(2.34))
</source>
Cela peut être utile pour les suites mais également dans des algorithmes qui se décomposent en tâches se découpant en sous-tâches de même nature.
 
== Fonction à l'intérieur d'une fonction ==
 
Il est possible de définir une fonction à l'intérieur d'une autre fonction. Dans ce cas, la fonction n'est visible qu'à l'intérieur de la fonction principale et ne peut être utilisée dans le champ global:
<source lang="py">
def f(x):
    from math import exp
    def g(y): return y**2+1
    return exp(g(x)) / g(x)
print(f(1), g(2))
</source>
Dans l'exemple, l'affichage de g(2) renvoie une erreur.
Cela peut être très pratique pour structure le corps d'une fonction complexe sans avoir à polluer le niveau global avec beaucoup de fonctions.
 
<!-- ''' En Python 3
Avec cette imbrication des fonctions, il se pose parfois le même problème de portée des variables qu'entre une fonction et le niveau global, problème résolu avec le mot-clé '''global'''. Dans le cas de fonctions imbriquées, on peut faire qu'une variable
<source lang="py">
def f(x):
    from math import exp
    a = 3.24
    def g(y):
        nonlocal a
        a += 2.43
        return y**2+1
     return exp(g(x)) / g(x)
print f(1), a
</source>
-->
 
== Passage d'arguments d'une fonction ==
 
Dans ce qui suit, arguments et paramètres seront considérés comme synonymes.
* f.__module__ : affiche le module de rattachement d'une fonction
* f.__name__ : affiche le nom de la fonction, par exemple si g = f, g.__name__ donne 'f'
 
'''Signature d'une fonction''': c'est la combinaison du nom et de la liste d'arguments
<source lang="py">
def f(a,b,c):
    return a+b**2+c**3
f(1,2,3)
print(f.__module__)
g = f
print(g.__name__)
</source>
définit localement les variables a,b,c qui prendront comme valeurs celles transmises lors de l'appel de la fonction, par exemple f(1,2,3). Il est obligatoire de donner la liste des arguments et dans le bon ordre.
 
'''Arguments optionnels''': il est possible de mettre des arguments optionnels à condition de leur donner une valeur par défaut et ils doivent nécessairement être placés après les arguments obligatoires:
<source lang="py">
def f(a,b,c=0):  # c est optionnel donc f(1,2) fonctionne et c prendra la valeur 0
    return a+b**2+c**3
print(f.__defaults__) # affiche les valeurs par défaut des arguments optionnels
def f(a=1,b=2,c=3):
    return a+b**2+c**3
print(f.__defaults__)
f(1,2,3), f(2)
</source>
 
'''Arguments nommés''': lorsqu'il y a des paramètres optionnels, on peut ne passer que l'argument dont on veut attribuer une valeur différente de la valeur par défaut, sans avoir à respecter l'ordre:
<source lang="py">
f(b=3)
f(c=2,a=2)
</source>
On parle de paramètres nommés. Cela donne une lecture claire de l'appel à la fonction. Si l'on utilise des arguments non nommés et des arguments nommés, ces derniers sont toujours placés à la fin:
<source lang="py">
f(1,c=3)
</source>
 
'''Liste extensible d'arguments non nommés''': Python permet à une fonction d'accepter une liste extensible d'arguments, c'est-à-dire que le nombre d'arguments n'est pas défini à l'avance. Il faudra dans ce cas veiller à ce que la fonction gère correctement cette liste quelconque d'arguments. La syntaxe est la suivante, avec une '''*''' devant le nom de ce qui sera un '''tuple''' contenant la liste des paramètres (on aime bien utiliser 'args' mais on peut appeler ce tuple autrement):
<source lang="py">
def f(*args):
    print(args)
    if len(args) == 3:
        a,b,c = args[0],args[1],args[2]
        return a+b+c
    else:
        return None
f(1,2,3)
f(3)
</source>
 
'''Liste extensible d'arguments nommés''': il est en fait plus judicieux d'utiliser une liste de paramètres nommés extensible. La syntaxe est la suivante, avec cette fois une double étoile '''**''' et 'kwargs' n'est alors plus un tuple mais un dictionnaire associant nom et valeur des variables à transmettre:
<source lang="py">
def f(**kwargs):
    print(str(kwargs))
    if len(kwargs) == 3:
        a,b,c = kwargs['a'],kwargs['b'],kwargs['c']
        return a+b**2+c**3
    else:
        return None
print(f(a=1,b=2,c=3), f(c=1,b=2,a=3))
print(f(c=2))
</source>
Là encore, le nommage rend clair l'appel de la fonction.
 
On peut mixer tous ces mécanismes de passage d'arguments mais en respectant l'ordre suivant: arguments obligatoires, optionnels, extensible non nommés puis extensible nommés soit:
<source lang="py">
def g(a,b=2,*args,**kwargs):
    print(a,b,args,kwargs)
g(1,2,3,4,5,x=3)
</source>
 
<!-- Du Python 3 et non 2.7
Cette hiérarchie permet par exemple de forcer le nommage d'un paramètre en insérant *args après les paramètres non-nommés avec la syntaxe
<source lang="py">
def rienfaire:
    pass
def comparer(s,t):
    return s<t
def f(a,b,fonction):
    return fonction(a,b)
def g(a,b,*args, fonction=rienfaire):
    return fonction(a,b)
f(1,2,comparer)
g(1,2,fonction=comparer)
print(g.__kwdefaults__)
</source>
L'argument fonction est alors considéré comme un argument nommé.
La fonction '''g()''' ne peut être appelée qu'avec l'argument 'fonction' explicitement nommé ce qui clarifie l'appel.
Dans l'exemple, on a aussi donné une valeur par défaut à cet argument. On peut faire afficher la liste de ce type d'arguments avec la commande:
print g.__kwdefaults__ : affiche les valeurs par défaut des arguments obligatoirement nommés
-->
 
L'utilisation des listes extensibles d'arguments requiert une bonne maîtrise de la programmation et n'est pas recommandée en première utilisation. Il est idéal d'avoir un nombre fini d'arguments nommés avec valeurs par défaut dans un premier temps. Cependant, ce mécanisme permet de transmettre des listes d'arguments de façon efficace et évolutive et s'avère utile dans la construction de bibliothèques de fonctions dépendantes les unes les autres.
 
'''Exemple''': La fonction '''plot()''' de '''matplotlib''' est déclarée avec la 'signature universelle'
<source lang="py">
def matplotlib.pyplot.plot(*args, **kwargs):
</source>
mais il est clair qu'en pratique, on ne pourra pas lui donner n'importe quoi comme arguments. Pour pouvoir comprendre quoi lui passer comme arguments, il faut lire sa [http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot documentation]. Essentiellement, les arguments non nommés doivent être des couples 'x,y' d'abscisses/ordonnées avec en option un argument donnant le format. Les arguments nommés sont des options et il faut donc clairement utiliser la syntaxe 'option=valeur' dans le passage d'argument pour qu'elle soit prise en compte.


f = lambda x: x**2
Exemples d'utilisation tirés de la documentation:
<source lang="py">
plot(x,y)
plot(x, y, 'bo')
plot(x1, y1, 'g^', x2, y2, 'g-')
plot(x, y, color='green', linestyle='dashed', marker='o', markerfacecolor='blue', markersize=12).
</source>
</source>


=== appliquer une fonction à une liste ===
== Documentation d'une fonction ==


On peut écrire aisément une documentation pour une fonction en écrivant sous la définition un texte insérer dans des triples guillemets.
On peut alors faire afficher la documentation à l'aide de la fonction '''help''' ou en faisant afficher l'attribut '''__doc__''' de la fonction qui conserve le texte.
<source lang="py">
<source lang="py">
# une suppose une fonction f() définie
def Sort(L, cmp):
# initialisation d'une liste
    """
A = [ i for i in range(10) ]
    Fonction Sort(L):
    prend une liste L en argument et la tri selon la méthode cmp.
    """
help(Sort)
print(Sort.__doc__)
</source>


# une première version lourde mais qui ressemble au C
== Appliquer une fonction à une liste ==
 
une suppose une fonction f() définie, par exemple
<source lang="py">
f = lambda x: x**2
A = list(range(10)) # initialisation d'une liste
</source>
une première version lourde mais qui ressemble au C
<source lang="py">
B = [ 0.0 ]*len(A)
B = [ 0.0 ]*len(A)
for i in range(len(A)):
for i in range(len(A)):
     B[i] = f(A[i])
     B[i] = f(A[i])
# une version similaire mais écrite en ligne
</source>
une version similaire mais écrite en ligne
<source lang="py">
B = [ f(A[i]) for i in range(len(A)) ]
B = [ f(A[i]) for i in range(len(A)) ]
 
</source>
# le même résultat en itérant directement sur les éléments de a
le même résultat en itérant directement sur les éléments de A
<source lang="py">
B = [ f(a) for a in A ]
B = [ f(a) for a in A ]
</source>
</source>
 
en utilisant la fonction '''map''' du standard Python
=== passage d'arguments d'une fonction ===
<source lang="py">
B = list(map(f,A))
</source>
enfin le tout en une ligne, possible uniquement avec une définition faite par le mot-clé '''lambda'''
<source lang="py">
B = list(map(lambda x: x**2,A))
</source>

Latest revision as of 12:35, 10 September 2016

<--Sommaire

Déclarer et appeler une fonction

Deux options suivant la complexité

1. définition usuelle à l'aide du mot-clé def et ouverture d'un bloc. return permet que la fonction renvoie un résultat mais ce n'est pas obligatoire, la fonction peut simplement manipuler les données en entrée

def f(x):
    return x**2

2. on peut également définir une fonction lambda ou 'fonction anonyme' selon

f = lambda x: x**2

dans l'exemple ci-dessus, le caractère anonyme n'est pas très clair car on lui a affecté la variable f pour l'utiliser avec f(x) comme précédemment le caractère anonyme apparaît lorsqu'on passe ce type de fonction en argument.

On demande l'exécution d'une fonction en utilisation les parenthèses () et en passant ses arguments à l'intérieur des parenthèses. Une fonction peut ne peut avoir d'argument et ne rien renvoyer :

def f():
    print("Hello")
f()     # appel de la fonction
f       # affiche l'objet fonction
g = f   # la variable g fait référence à la fonction f
g()     # même résultat que l'appel de f
a = f() # la fonction f() est exécutée mais comme elle ne
print(a)# renvoie rien, a est de type None

Récursivité

Une fonction peut s'appeler elle-même

def f(x):
    if x < 10: return x*f(x+1)
    else: return 10
print(f(1), f(10), f(9), f(2.34))

Cela peut être utile pour les suites mais également dans des algorithmes qui se décomposent en tâches se découpant en sous-tâches de même nature.

Fonction à l'intérieur d'une fonction

Il est possible de définir une fonction à l'intérieur d'une autre fonction. Dans ce cas, la fonction n'est visible qu'à l'intérieur de la fonction principale et ne peut être utilisée dans le champ global:

def f(x):
    from math import exp
    def g(y): return y**2+1
    return exp(g(x)) / g(x)
print(f(1), g(2))

Dans l'exemple, l'affichage de g(2) renvoie une erreur. Cela peut être très pratique pour structure le corps d'une fonction complexe sans avoir à polluer le niveau global avec beaucoup de fonctions.


Passage d'arguments d'une fonction

Dans ce qui suit, arguments et paramètres seront considérés comme synonymes.

  • f.__module__ : affiche le module de rattachement d'une fonction
  • f.__name__ : affiche le nom de la fonction, par exemple si g = f, g.__name__ donne 'f'

Signature d'une fonction: c'est la combinaison du nom et de la liste d'arguments

def f(a,b,c): 
    return a+b**2+c**3
f(1,2,3)
print(f.__module__)
g = f
print(g.__name__)

définit localement les variables a,b,c qui prendront comme valeurs celles transmises lors de l'appel de la fonction, par exemple f(1,2,3). Il est obligatoire de donner la liste des arguments et dans le bon ordre.

Arguments optionnels: il est possible de mettre des arguments optionnels à condition de leur donner une valeur par défaut et ils doivent nécessairement être placés après les arguments obligatoires:

def f(a,b,c=0):  # c est optionnel donc f(1,2) fonctionne et c prendra la valeur 0
    return a+b**2+c**3
print(f.__defaults__) # affiche les valeurs par défaut des arguments optionnels
def f(a=1,b=2,c=3):
    return a+b**2+c**3
print(f.__defaults__)
f(1,2,3), f(2)

Arguments nommés: lorsqu'il y a des paramètres optionnels, on peut ne passer que l'argument dont on veut attribuer une valeur différente de la valeur par défaut, sans avoir à respecter l'ordre:

f(b=3)
f(c=2,a=2)

On parle de paramètres nommés. Cela donne une lecture claire de l'appel à la fonction. Si l'on utilise des arguments non nommés et des arguments nommés, ces derniers sont toujours placés à la fin:

f(1,c=3)

Liste extensible d'arguments non nommés: Python permet à une fonction d'accepter une liste extensible d'arguments, c'est-à-dire que le nombre d'arguments n'est pas défini à l'avance. Il faudra dans ce cas veiller à ce que la fonction gère correctement cette liste quelconque d'arguments. La syntaxe est la suivante, avec une * devant le nom de ce qui sera un tuple contenant la liste des paramètres (on aime bien utiliser 'args' mais on peut appeler ce tuple autrement):

def f(*args):
    print(args)
    if len(args) == 3:
        a,b,c = args[0],args[1],args[2]
        return a+b+c
    else:
        return None
f(1,2,3)
f(3)

Liste extensible d'arguments nommés: il est en fait plus judicieux d'utiliser une liste de paramètres nommés extensible. La syntaxe est la suivante, avec cette fois une double étoile ** et 'kwargs' n'est alors plus un tuple mais un dictionnaire associant nom et valeur des variables à transmettre:

def f(**kwargs):
    print(str(kwargs))
    if len(kwargs) == 3:
        a,b,c = kwargs['a'],kwargs['b'],kwargs['c']
        return a+b**2+c**3
    else:
        return None
print(f(a=1,b=2,c=3), f(c=1,b=2,a=3))
print(f(c=2))

Là encore, le nommage rend clair l'appel de la fonction.

On peut mixer tous ces mécanismes de passage d'arguments mais en respectant l'ordre suivant: arguments obligatoires, optionnels, extensible non nommés puis extensible nommés soit:

def g(a,b=2,*args,**kwargs):
    print(a,b,args,kwargs)
g(1,2,3,4,5,x=3)


L'utilisation des listes extensibles d'arguments requiert une bonne maîtrise de la programmation et n'est pas recommandée en première utilisation. Il est idéal d'avoir un nombre fini d'arguments nommés avec valeurs par défaut dans un premier temps. Cependant, ce mécanisme permet de transmettre des listes d'arguments de façon efficace et évolutive et s'avère utile dans la construction de bibliothèques de fonctions dépendantes les unes les autres.

Exemple: La fonction plot() de matplotlib est déclarée avec la 'signature universelle'

def matplotlib.pyplot.plot(*args, **kwargs):

mais il est clair qu'en pratique, on ne pourra pas lui donner n'importe quoi comme arguments. Pour pouvoir comprendre quoi lui passer comme arguments, il faut lire sa documentation. Essentiellement, les arguments non nommés doivent être des couples 'x,y' d'abscisses/ordonnées avec en option un argument donnant le format. Les arguments nommés sont des options et il faut donc clairement utiliser la syntaxe 'option=valeur' dans le passage d'argument pour qu'elle soit prise en compte.

Exemples d'utilisation tirés de la documentation:

plot(x,y)
plot(x, y, 'bo')
plot(x1, y1, 'g^', x2, y2, 'g-')
plot(x, y, color='green', linestyle='dashed', marker='o', markerfacecolor='blue', markersize=12).

Documentation d'une fonction

On peut écrire aisément une documentation pour une fonction en écrivant sous la définition un texte insérer dans des triples guillemets. On peut alors faire afficher la documentation à l'aide de la fonction help ou en faisant afficher l'attribut __doc__ de la fonction qui conserve le texte.

def Sort(L, cmp):
    """
    Fonction Sort(L): 
    prend une liste L en argument et la tri selon la méthode cmp.
    """
help(Sort)
print(Sort.__doc__)

Appliquer une fonction à une liste

une suppose une fonction f() définie, par exemple

f = lambda x: x**2
A = list(range(10)) # initialisation d'une liste

une première version lourde mais qui ressemble au C

B = [ 0.0 ]*len(A)
for i in range(len(A)):
    B[i] = f(A[i])

une version similaire mais écrite en ligne

B = [ f(A[i]) for i in range(len(A)) ]

le même résultat en itérant directement sur les éléments de A

B = [ f(a) for a in A ]

en utilisant la fonction map du standard Python

B = list(map(f,A))

enfin le tout en une ligne, possible uniquement avec une définition faite par le mot-clé lambda

B = list(map(lambda x: x**2,A))