AMU :: Polytech Biotech 3A :: JGB53D :: 2014/2015 :: Jacques van Helden, Denis Puthier, Nicolas Terrapon

Contenu

Outre les instructions procédurales que vous avez découvert lors des scéances précédentes, les langages de programmation vous offrent la possibilité de créer vos propres fonctions. L'intérêt d'une fonction est de regrouper une série d'instructions que l'on souhaite répéter plus d'une fois dans un programme ou un projet. En remplaçant certaines parties de code par un appelle à une fonction dont le nom a été judicieusement choisi, on améliore aussi beaucoup la lisibilité du code.


Fonctions

Définition et appel

La création d'une fonction se fait grâce au mot-clé def. Celui-ci est suivi du nom de la fonction et du caractère ":". On décrit ensuite les opérations qui doivent être effectuées par la fonction en marquant l'indentation. Python crée alors un objet function portant le nom choisi pour la fonction. Le programme python peut alors exécuter, ou appeler cette fonction.

In [13]:
##################################
##  Exemple de fonction simple  ##
##################################

def print_hello():
    print("Hello!")
In [14]:
print_hello()
Hello!

In [15]:
##################################
##  print_hello est un objet     ##
## 'function'
##################################

type(print_hello)
Out[15]:
function

Passage d'arguments

Une fonction dépendent fréquemment de paramètres, ou arguments, nécessaires à la réalisation de la fonction mais qui peuvent varier lors des différents appels à cette fonction. Pour spécifier les arguments d'une fonction, vous devez les lister entre parenthèses après son nom.

In [16]:
################################################
##  Exemple de fonction simple avec arguments ##
################################################

def print_hello(n=10): # NOTEZ LE ":" et le mot clef "def"
    print(n * "Hello!")

# Si on appelle print hello sans argument
# la chaîne de caracatère Hello est imprimé 10 fois
print_hello()

# on peut cependant modifier la valeur de l'argument
# lors de l'appelle de la fonction. Si on positionne
# n sur 5 alors Hello sera imprimé 5 fois.
print_hello(n=5)

# On peut éventuellement ne pas spécifier le nom de l'argument
# (Cf plus bas)
print_hello(5)
Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!Hello!
Hello!Hello!Hello!Hello!Hello!
Hello!Hello!Hello!Hello!Hello!

In [17]:
##################################################
##  La fonction peut avoir plusieurs arguments  ##
##################################################

def print_mot(mot="Hello", n=5):
    print(n * mot)

print_mot("foo", n=6)
foofoofoofoofoofoo

In [18]:
##############################################
##  Choississez judicieusement les noms de  ##
## vos fonctions afin d'améliorer la        ##
## lisibilité de votre code                 ##
##############################################
# On définie la fonction suivante comportant une expression
# régulière (dont on sait qu'elles peuvent être plus ou moins 
# lisibles parfois). Ici, la fonction test si la ligne commence
# par '>'. On l'utiliserait lors de la lecture d'un fichier fasta.
import re
def is_fasta_header(string):
    """Check if the line is a fasta header."""
    res = re.match(r'^>', string)
    return res


# Dans le programme une variable doit être testée,
# disons la ligne courante qui peut être, dans le cas 
# d'un fichier fasta, l'entête (">...") ou la séquence.
line = ">AI35B34 - gene A"
# Dans le code de notre programme on utilisera une fonction
# dont le nom, judicieusement choisi nous rapproche du langage naturel.

if is_fasta_header(line):
    print("Lecture de l'entête")
Lecture de l'entête

Passage d'arguments par noms

Il est tout d'abord possible, lors de l'appel à la fonction, de d'utiliser des noms d'arguments. Ceci est pratique voire nécessaire quand la fonction comporte un nombre relativement important d'arguments. Par ailleurs, il n'est, alors, plus obligatoire de respecter l'ordre établi des arguments. Vous pouvez également spécifier une valeur par défault pour vos arguments. Le passage de ces arguments lors de l'appel de la fonction devient alors facultatif. Finalement, il est possible pour une fonction de recevoir un nombre d'arguments arbitraire: inconnu à l'avance et pouvant être différent à chaque appel.

In [19]:
###################################
##  Exemple d'appel par nom      ##
## d'arguments                   ##
###################################

def print_mot(mot="foo", n=10, sep="-"):
      print(sep.join(n * [mot]))

####################################################
##  Exemple d'appel avec valeurs par défault      ##
####################################################
# Renvoie la même chose que 
print_mot() 

print_mot(mot="foo", n=10, sep="-")

####################################################
##  En modifiant les arguments                    ##
####################################################

print_mot(mot="bla", n=3, sep="/")
print_mot(mot="bar", n=5, sep="|")

####################################################
##  En modifiant la position des arguments        ##
####################################################
# on change la position des arguments en les nommant
print_mot(n=3, mot="bla", sep="/")

# équivalent à print_mot(mot="bla", n=3, sep="/")
print_mot(mot="bla", n=3, sep="/")

# équivalent aussi à print_mot(3,"bla", "/")
print_mot("bla", 3, "/")
foo-foo-foo-foo-foo-foo-foo-foo-foo-foo
foo-foo-foo-foo-foo-foo-foo-foo-foo-foo
bla/bla/bla
bar|bar|bar|bar|bar
bla/bla/bla
bla/bla/bla
bla/bla/bla

In [20]:
# attention, si les arguments ne sont pas
# nommés on ne pourra pas les mettre 
# dans n'importe quel ordre.
# Ici, l'instruction renvoie une erreur.

print_mot(3, "bla", "/") # on ne peut pas multiplier une list par un str.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-20-49dce2073ffc> in <module>()
      4 # Ici, l'instruction renvoie une erreur.
      5 
----> 6 print_mot(3, "bla", "/") # on ne peut pas multiplier une list par un str.

<ipython-input-19-c0d3599a092a> in print_mot(mot, n, sep)
      5 
      6 def print_mot(mot="foo", n=10, sep="-"):
----> 7       print(sep.join(n * [mot]))
      8 
      9 ####################################################

TypeError: can't multiply sequence by non-int of type 'list'

Variable retour

Une fonction est parfois créé pour manipuler des données et réaliser des calculs que l'on souhaite réutiliser par la suite. Dans ce cas, le simple affichage du résultat ne nous satisfera pas. On peut alors demander à la fonction de renvoyer son résultat avec le mot-clef return. Lors de l'appel à la fonction, ce résultat pourra alors être conserver dans une variable (par affectation). Finalement, Python permet de renvoyer plusieurs résultats sous la forme d'un tuple.

In [21]:
###############################################
##  Exemple de fonctions avec retour simple  ##
###############################################


def return_mot(mot="foo", n=10, sep="-"):
      return(sep.join(n * [mot])) # on substitue print par return

# Le résultat est renvoyé à l'écran
return_mot()

# Cependant, le résultat peut être renvoyé et stocké dans un variable
# Cette variable pourra être utilisé par la suite.
a = return_mot()

# Ici, le contenu est stocké dans a
print(a)
foo-foo-foo-foo-foo-foo-foo-foo-foo-foo

In [22]:
###############################################
##  Exemple de fonctions avec renvoie        ##
## de plusieurs objets                       ##
###############################################
# Si la fonction renvoie plusieurs objects
# ceux si seront renvoyés par défaut sous la forme d'un tuple

def return_mot(mot="foo", n=10, sep="-"):
      var_1 = sep.join(n * [mot])     # on multiplie mot n fois avec le séparateur sep
      var_2 = sep.join([mot, str(n)]) # on renvoie n et mot séparés par sep
      return var_1, var_2             # on renvoie les deux résultats
    
a = return_mot()
print(type(a))
print(a)
print(a[0])
print(a[1])
<class 'tuple'>
('foo-foo-foo-foo-foo-foo-foo-foo-foo-foo', 'foo-10')
foo-foo-foo-foo-foo-foo-foo-foo-foo-foo
foo-10

Portée des variables

Dans une fonction, toute affectation à une variable revient à créer une nouvelle variable de portée locale (i.e connue dans l'environnement d'execution de la fonction mais pas dans l'environnement global).

In [23]:
##################################
##  Exemple de variable locale  ##
##################################

x = 100

def print_x_value():
    x = 8 # x est défini localement
    print("Valeur dans l'environnement de la fonction = ", x)

print_x_value()

print("Valeur dans l'environnement global = ", x)
Valeur dans l'environnement de la fonction =  8
Valeur dans l'environnement global =  100

Réutilisation du code

Il est souvent conseillé d'écrire un programme sous la forme d'une fonction elle même appelée dans le corps du programme. En effet, cela permet à terme d'appeler cette même fonction depuis un autre programme. Par exemple, pour le programme cut que nous avons écrit (cf exercices pratiques), il faudrait mieux écrire:

Ici une structure conditionnelle teste la valeur d'une variable spéciale de Python appelée "name". Si le code est effectué à partir du fichier lui même (e.g python3 cut.py) la variable vaudra "main". Si le programme est appelé via un "import" la variable "name" prendra comme valeur le nom du fichier.

# -*- coding: utf-8 -*- # La ligne précédente permet d'utiliser des caractères accentués # dans les commentaires """ A program that retrieve a column """ def cut(file_name=None, # Le nom du fichier col_to_print=None, #Le numéro de colonne à imprimer sep="\t") : # Le séparateur de colonnes # On ouvre le fichier # Adaptez le chemin en fonction de la localisation du # file_handler est un objet 'file'. C'est un objet capable de lire # les lignes d'un fichier. file_handler = open(file_name, "r") # Dans la structure for ci-dessous, # line est une variable renvoyée # par file_handler. On parcours donc # le fichier ligne à ligne for line in file_handler: # A chaque itération/ligne, # On découpe la chaîne de caractère # sur le séparateur tabulation (\t). column = line.split(sep) # column[0] si l'on veut la première colonne print(column[ col_to_print - 1]) if __name__ == "__main__": cut("../data/hg38_5k.gtf", 4)