Skip to article frontmatterSkip to article content

objectifs de cette section

dans cette partir consacrée aux fonctions, nous allons approfondir le sujet, et notamment creuser

mais pour commencer, voici quelques rappels et généralités sur ce thème

pour réutiliser du code en python

DRY = don’t repeat yourself : cut’n paste is evil

fonctions

pas d’état après exécution

modules

garde l’état, une seule instance par programme

classes

instances multiples, chacune garde l’état, héritage

comment définir une fonction ?

syntaxe

def name(arg1, arg2, .. argN):
    <statement>
    return <value>   # peut apparaitre n’importe où
# pas du tout usuel, mais pour bien comprendre :
# on peut parfaitement écrire ceci
if test:
    def func():
        ...
else:
    def func():
        ...
func() # exécute une version qui dépend du test

duck typing

il n’y a pas de typage statique en Python: on ne sait pas de quel type doivent être x et y, et tant que ça fait du sens au moment de l’exécution, le code est correct ! on appelle ça aussi le duck typing

# on va pouvoir appeler cette fonction ...
def times(x, y):
    return x * y
# ... aveec deux entiers
times(2, 3)
6
# ou avec deux floats
times(1.6, 9)
14.4
#  ... et même comme ceci !
times('-spam-', 4)
'-spam--spam--spam--spam-'

type hints

si on le souhaite, on peut indiquer le type des paramètres attendus et du résultat

def type_hints_1(x: int, y: float) -> str:
    """
    pour des types simples
    """
    ...
# un peu plus compliqué (depuis la 3.9)

def type_hints_2(x: tuple[int, str, bool],
                 y: dict[str, list[int]]) -> None:
    ...

il faut savoir que c’est totalement optionnel et que ça ne modifie pas le comportement du code

à quoi ça sert du coup, me direz-vous ? eh bien surtout à deux choses

on reparle plus en profondeur des type hints ici

un objet comme un autre

voici à nouveau un exemple biscornu; ce n’est évidemment pas recommandé, mais pour bien comprendre, sachez que c’est légal de faire ceci:

# la fonction est un objet 
times
<function __main__.times(x, y)>
# pas du tout recommandé, mais 
# on peut affecter cet objet à une autre variable !
foo = times

# et donc maintenant foo désigne une fonction, je peux donc l'appeler
foo(3, 14)
42
# et redéfinir `times` pour faire + à la place de * !
def times(x, y):
    # ne vraiment pas faire ça en vrai !!
    return x + y
# maintenant times fait une addition !
times(3, 4)
7
# et foo fait bien la multiplication
foo(3, 4)
12

l’instuction return

les docstrings

où documenter ?

si, dans un objet de type fonction, classe ou module, la première instruction est une chaîne de caractères
alors c’est le docstring de cet objet, qui constitue sa documentation

l’idée étant naturellement de pouvoir maintenir en même temps le code et la doc, plutôt que d’avoir la doc dans un système séparé qui du coup n’est jamais à jour

def hyperbolic(x, y):
    """
    computes xˆ2 - y^2
    """
    return x**2 - y**2

c’est ce qui est utilisé pour afficher la doc avec help(objet)

help(hyperbolic)
Help on function hyperbolic in module __main__:

hyperbolic(x, y)
    computes xˆ2 - y^2

comment documenter ?

c’est une bonne habitude de toujours documenter !

format et exemple

PEP8

d’après la PEP8, on doit

passage d’arguments et références partagées

# nous allons illustrer ce mécanisme de 
# références partagées grâce à pythontutor.com

%load_ext ipythontutor
%%ipythontutor curInstr=2 width=1000 height=400

# les arguments d'une fonction sont toujours passés par référence
liste = [1, 2, 3]

def mess_with(reference):
    reference[1] = 100
    
mess_with(liste)
    
print(liste)
Loading...
%%ipythontutor width=800 height=450 heapPrimitives=true
def mess_with2(a, b):
    a = 3          # ceci n'aura pas de conséquence sur A
    b[0] = 'boom'  # ceci va changer B

A = 1      # immutable ne peut pas être modifiée
B = [10, 20] # mutable, l'objet liste est modifié par
           # changer() par une modification in-place
mess_with2(A, B)
print(A)
print(B)
Loading...