mardi 5 avril 2011

Les fonctions en Python

Introduction
Une fonction c'est une fonction et fonctionne selon les mêmes principes dans tous les langages de programmation.
Cependant, chaque langage (Delphi, C#, C++) apporte ses propres spécificités et Python apporte également les siennes mais toujours dans l'esprit de la simplicité.

Ainsi, les fonctions Python:
  • Acceptent des valeurs par défaut (comme beaucoup de langage).
  • Permet de retourner plusieurs valeurs!
  • Peut accepter un nombre indéterminé d'arguments (non nommés).
  • Permet de nommer les arguments assignés ou de fournir des arguments complémentaires nommés.
Description générale d'une fonction

def ma_fonction( argument, argument_optionel = -1, *args, **kwargs ):
  • argument - argument obligatoire, oublier sa valeur lors de l'appel de la fonction provoquera une erreur.
  • argument_optionel - argument avec une valeur par défaut. L'argument est optionnel, ainsi, si l'on ne précise pas sa valeur lors de l'appel, c'est la valeur par défaut qui sera utilisée.
  • *args - liste d'argument non nommés. Ils sont ajoutés en en fin d'appel de fonction... un exemple typique est la définition d'une fonction sum acceptant un nombre illimité d'arguments.
    Ex: sum( 1, 5, 7 ) + sum( 1, 3, 5, 48, 78, 47)
    La liste d'argument non nommé succède les arguments obligatoires et les arguments optionnels.
  • **kwargs - liste contenant les arguments nommés "keyword args". Il est ainsi possible d'ajouter à la volée des arguments identifiés par leur nom d'argument. J'évoquais plus haut le nom d'une colonne d'une feuille Excel.
Exemples pratiques
Utiliser la valeur par défaut
>>> def maFonction( a, b, c = -1 ):
...     print( "a=%i, b=%i, c=%i" % (a,b,c) )
... 
>>> maFonction(7,5)
a=7, b=5, c=-1
>>> maFonction(7,5,128)
a=7, b=5, c=128

Retourner plusieurs valeurs
Python est capable de retourner plusieurs valeurs en une seule ligne de commande.
Ces valeurs peuvent être récupérée dans différentes variables ou dans un tuple.
>>> def maFonction2( a, b, c):
...     return a+b, b+c, a-c
... 
>>> maFonction2( 10, 5, 3 )
(15, 8, 7)

>>> # récupération dans des variables 
>>> aa, bb, cc = maFonction2( 10, 5, 3 )
>>> print( "aa=%i, bb=%i, cc=%i" % (aa, bb, cc) )
aa=15, bb=8, cc=7

>>> # récupération via un Tuple 
>>> resultat = maFonction2( 10, 5, 3 )
>>> print( "aa=%i, bb=%i, cc=%i" % resultat )
aa=15, bb=8, cc=7
>>> print( "bb=%i" % ( resultat[1] ) )
bb=8 

*args - Accepter un nombre indéterminé d'arguments
Si cela peu paraître inutile, l'opportunité d'ajouter un nombre illimité de paramètre en fin de fonction peu s'avérer terriblement utile pour des fonctions de traitement.
Pour les puristes, il est certes possible de concevoir sa fonction pour imposer un argument de type liste... mais cela n'est pas des plus commodes à utiliser et alourdir inutilement l'utilisation de la syntaxe.

Voici l'exemple d'une fonction mySum capable de sommer un nombre illimité d'arguments.

>>> def mySum( *args ):
...     result = 0
...     for item in args:
...             result += item
...     return result
... 
>>> mySum( 1, 2, 3 )
6 

Plus d'information concernant *args et **kwargs sont disponible dans l'article "How to use *args and **kwargs in Python".

**kwargs - Utiliser les arguments nommés
Il est possible de fournir un ou plusieurs arguments (nom + valeur) à une fonction alors que ces derniers ne se trouvent pas dans la liste des paramètres.

Cela permet par exemple de mettre en place des recherche dynamique sur un contenu/données tout aussi dynamique... c'est à la dernière minute que l'on vérifiera si l'argument (identifier par son nom) existe bien dans les données à explorer (par exemple le nom d'une colonne d'une fiche Excel).

A noter que si l'on nomme les arguments obligatoire dans l'appel de fonction, il n'est alors plus possible d'utiliser les arguments complémentaires non nommées (*args)

Voici un exemple sommaire d'utilisation de **kwargs.
>>> def myKwTest( **kwargs ):
...     for key in kwargs:
...             print( "%s = %s" % (key, kwargs[key] ) )
... 
>>> myKwTest( a=12, violette="bonbon", homer="Simpson", age=38 )
a = 12
violette = bonbon
age = 38
homer = Simpson 

Par exemple, l'utilisation de **kwargs permettrait d'écrire une fonction retrouvant un noeud dans un document xml, noeud qui devrait avoir une valeur particulière pour un ou plusieurs attributs (à la fonction à adapter son fonctionnement)
Voici code rudimentaire mettant en oeuvre ce principe d'appel
>>> def findNode( tagName, **attrs_kwargs ):
...     pass
... 
>>> aNode = findNode( "invoice", customerId=125487, orderNr=128 )
>>> aNode2 = findNode( "invoice", invoiceNr="2011/04/03-12748" ) 

BeautifulSoup décrit dans l'article "BeautifulSoup - comment extraire ou manipuler une page html en Python" utilise d'ailleurs **kwargs pour faciliter l'écriture des recherche de noeud dans les documents.

Plus d'information concernant *args et **kwargs sont disponible dans l'article "How to use *args and **kwargs in Python".

Extra: Assignation d'argument via dictionnaire
Il est possible d'assigner les différents arguments d'une fonction en utilisant un dictionnaire. Ce dernier reprendra alors le nom de l'argument et sa valeur.
Notez la syntaxe du ** devant le dictionnaire lors de l'appel de la fonction.

>>> def myFunctionSimple( a, b, c=128 ):
...     print( "a=%i, b=%i, c=%i" % (a,b,c) )
... 
>>> myFunctionSimple( **{"b":007, "a":4587} )
a=4587, b=7, c=128
>>> myDico = { "a":12, "b":120, "c":1200 }
>>> myFunctionSimple( **myDico )
a=12, b=120, c=1200

Le dictionnaire peut bien entendu être utilisé pour fournir des arguments complémentaires qui seront soit utilisé par *args, soit par **kwargs.

>>> def myFunction3( a, b, c=1, **kwargs ):
...     print( "a=%i, b=%i, c=%i" % (a,b,c) )
...     for key in kwargs:
...             print( "kwargs... %s = %s" % (key, kwargs[key]) )
... 
>>> # Utilisation d'un dictionnaire pour assigner les valeurs
>>> #    des paramètres obligatoires et optionnels.
>>> myFunction3( **{"a":12, "zz":"moustique", "bzzz":"mouche", "b":-1} )
a=12, b=-1, c=1
kwargs... bzzz = mouche
kwargs... zz = moustique

Un exemple complet
Voici un exemple complet issus de Linux Magazine France Hors-série entièrement dédié à Python (no 53 parution mars/avril 2011)

Soit la fonction myFunc affichant le valeur de tous les paramètres.

def myFunc( datas, opt_datas=[], *args, **kwargs ):
    for d in datas:
        print( "datas: %s" % (d) )
    for d in opt_datas:
        print( "opt_datas: %s" % (d) )
    i = 0
    for a in args:
             i += 1
        print( "exploring args[%i]..." % (i) ) 
        for d in a:
            print( "args[%i] has data %s" % (i,d) )
    i = 0
    for key in kwargs:
        i += 1
        print( "exploring kwargs[\"%s\"]..." % (key) )
        values = kwargs[key]
        for d in values:
            print( "kwargs[\"%s\"] has data %s" % (key, d) ) 

Et différents appels de fonction dont on pourra inspecter les résultats
myFunc( [1,2] )
myFunc( [1,2], [3,4] )
myFunc( [1,2], [3,4], [4,6] )
myFunc( [1,2], [3,4], [4,6], [7,8] )
# ---------------------------------------------
myFunc( datas=[1,2] )
myFunc( datas=[1,2], opt_datas=[3,4] )
myFunc( datas=[1,2], opt_datas=[3,4], brol=[4,6] )
myFunc( datas=[1,2], opt_datas=[3,4], brol=[4,6], machin=[7,8] )
# ---------------------------------------------
myFunc( [1,2], opt_datas=[3,4] )
myFunc( [1,2], opt_datas=[3,4], brol=[4,6] )
myFunc( [1,2], [3,4], machin=[7,8] )
myFunc( [1,2], [3,4], [4,6], machin=[7,8] ) 

Le code source complet est disponible dans le fichier functionDemo.py (exécutable en ligne de commande).
Le résultat complet est disponible dans le fichier functionDemo.result

Aucun commentaire: