Python prend en charge un large éventail de styles de programmation, notamment procédural (impératif), orienté objet et fonctionnel. Bien que la programmation orientée objet et la programmation fonctionnelle soient de puissants paradigmes de programmation, en particulier dans les logiciels volumineux et complexes, la procédure est souvent à la fois plus facile à comprendre et à représenter directement une formule mathématique. L’idée de base de la programmation procédurale est de produire une fonction ou un ensemble de fonctions (génériquement) de la forme
$$y = f(x)$$Les fonctions prennent une ou plusieurs entrées et produisent une ou plusieurs sorties.
Les fonctions Python sont très simples à déclarer et peuvent se trouver dans le même fichier que le programme principal ou dans un programme standard. Les fonctions sont déclarées à l’aide du mot clé def
et la valeur produite est renvoyée à l’aide du
mot clé return
. Considérons une fonction simple qui renvoie le carré de l’entrée, $y = x^2$.
def square(x):
return x**2
# appel de la fonction
x = 2
y = square(x)
print(x,y)
Un autre exemple
def l2distance(x,y):
return (x-y)**2
# appel de la fonction
x = 3
y = 10
z = l2distance(x,y)
print(x,y,z)
La fonction peut également être définie à l'aide de tableaux et de matrices NumPy.
import numpy as np
def l2_norm(x,y):
d = x - y
return np.sqrt(d @ d)
# appel de la fonction
x = np.random.randn(10)
y = np.random.randn(10)
z = l2_norm(x,y)
print(x-y)
print("La distance L2 ",z)
Lorsque plusieurs sorties sont renvoyées mais qu'une seule variable est disponible pour l'affectation, toutes les sorties sont renvoyées dans un tuple. Alternativement, les sorties peuvent être directement affectées lorsque la fonction est appelée avec le même nombre de variables que les sorties.
import numpy as np
def l1_l2_norm(x,y):
d = x - y
return sum(np.abs(d)),np.sqrt(d @ d)
# appels de la fonction
x = np.random.randn(10)
y = np.random.randn(10)
# Avec un appel à une seul objet de sortie
z = l1_l2_norm(x,y)
print(x-y)
print("La distance L1 est ",z[0])
print("La distance L2 est ",z[1])
# Avec un appel avec 2 objets de sortie
l1,l2 = l1_l2_norm(x,y)
print("La distance L1 est ",l1)
print("La distance L2 est ",l2)
Toutes les variables d'entrée dans les fonctions sont automatiquement des arguments mot-clés. On peut ainsi accéder à la fonction en plaçant les entrées dans l'ordre dans lequel elles apparaissent dans la fonction ou en appelant l'entrée par leur nom à l'aide de mot-clé = valeur
.
import numpy as np
def lp_norm(x,y,p):
d = x - y
return sum(abs(d)**p)**(1/p)
# appels de la fonction
x = np.random.randn(10)
y = np.random.randn(10)
z1 = lp_norm(x,y,2)
z2 = lp_norm(p=2,x=x,y=y)
print("Les distances Lp sont ",z1,z2)
Comme les noms de variable sont automatiquement des mots-clés, il est important d'utiliser des noms de variable significatifs lorsque cela est possible, plutôt que des variables génériques telles que $a, b, c$ ou $x, y$ et $z$. Dans certains cas, $x$ peut être raisonnable, mais dans l'exemple précédent qui calculait la norme $L_p$, appeler la troisième entrée $z$ serait une mauvaise idée.
Les valeurs par défaut sont définies dans la déclaration de fonction à l'aide de la syntaxe input = default
.
import numpy as np
def lp_norm(x,y,p = 2):
d = x - y
return sum(abs(d)**p)**(1/p)
# appel de la fonction
x = np.random.randn(10)
y = np.random.randn(10)
# Les arguments de la fonction (les valeurs par défaut peuvent être ignorées)
l2 = lp_norm(x,y)
l1 = lp_norm(x,y,1)
print("Les distances l1 et l2 sont ",l1,l2)
print("Vérification ", sum(abs(x-y))==l1)
Quelques bonnes pratiques
import numpy as np
def bad_function(x = np.zeros(1)):
print(x)
x[0] = np.random.randn(1)
# appels
bad_function()
bad_function()
Chaque appel à bad_function
indique que $x$ a une valeur différente bien que la valeur par défaut soit $0$. La solution à ce problème consiste à initialiser les objets mutables sur None
, puis à utiliser un if
pour vérifier et initialiser uniquement si la valeur est None
. Noter que les tests pour None
utilisent le mot-clé is
plutôt que le test d'égalité avec ==
.
import numpy as np
def good_function(x = None):
if x is None:
x = np.zeros(1)
print(x)
x[0] = np.random.randn(1)
# appels
good_function()
good_function()
En utilisant *args
import numpy as np
def lp_norm(x,y,p = 2, *args):
d = x - y
print('The L' + str(p) + ' distance is :', sum(abs(d)**p)**(1/p))
out = [sum(abs(d)**p)**(1/p)]
print('Number of *args:', len(args))
for p in args:
print('The L' + str(p) + ' distance is :', sum(abs(d)**p)**(1/p))
out.append(sum(abs(d)**p)**(1/p))
return tuple(out)
# appels de la fonction
x = np.random.randn(10)
y = np.random.randn(10)
# x & y sont nécessaires comme arguemnts (les optionnels sont dans *args)
lp = lp_norm(x,y)
# 3 arguments (donc rien dans *args)
lp = lp_norm(x,y,1)
# Avec des arguments optionnels
lp = lp_norm(x,y,1,2,3,4,1.5,2.5,0.5)
En utilisant **kwargs
import numpy as np
def lp_norm(x,y,p = 2, **kwargs):
d = x - y
print('Number of *kwargs:', len(kwargs))
for key in kwargs:
print('Key :', key, ' Value:', kwargs[key])
return sum(abs(d)**p)
# appel de la fonction
x = np.random.randn(10)
y = np.random.randn(10)
# avec des arguments optionnels
lp = lp_norm(x,y,kword1=1,kword2=3.2)
# L'argument p est dans la définition de la fonction donc n'est pas considéré comme **kwargs
lp = lp_norm(x,y,toto1=1,toto2=3.2,p=0)
La chaîne de caractères de documentation est l'un des éléments les plus importants de toute fonction - en particulier une fonction écrite pour être utilisée par d'autres. La chaîne de documentation est une chaîne spéciale, entourée de guillemets triples, soit
'''
, soit """
, disponible à l'aide de help()
. Lorsque help(fun)
est appelé (ou fun?
/?fun
dans IPython), Python cherche la chaîne de caractères de documentation qui est placée juste en dessous de la définition de la fonction.
import numpy as np
def lp_norm(x,y,p = 2):
""" Le docstring contient l'aide de la fonction.
Un bon docstring doit expliquer les entrées et sorties,
et fournit un exemple.
"""
d = x - y
return sum(abs(d)**p)
help(lp_norm)
Cette chaîne de caractère de documentation n'est pas un bon exemple. Je suggère de suivre les instructions de NumPy, actuellement disponibles dans le référentiel source de NumPy(ou de rechercher numpy docstring). Voir aussi [NumPy example.py] (https://github.com/numpy/numpy/blob/master/doc/example.py) Celles-ci diffèrent des directives Python docstring guidelines et sont plus spécialisées que celles-ci, et sont plus appropriées pour le code numérique. Une meilleure chaîne de caractères de documentation pour lp_norm
serait:
import numpy as np
def lp_norm(x,y,p = 2):
""" Calcul de distance entre vecteurs.
La distance associée à la norme Lp est sum(abs(x-y)**p)**(1/p)
Paramètres
----------
x : ndarray
Premier argument First argument
y : ndarray
Second argument Second argument
p : float, optionnel
Puissance utilisée pour le cacul de la distance >=0
Sorties
-------
sortie : scalaire
Retourne la distance associée à la norme Lp entre x et y
Notes
-----
Pour p>=1, la sortie est décrite précédemment.
Pour 0<=p<1, la sortie est sum(abs(x-y)**p).
Exemples
--------
>>> x=[0,1,2]
>>> y=[1,2,3]
La norme L2 est par défaut
>>> lp_norm(x,y)
Lp peut être calculée en utilisant la 3 ème option
>>> lp_norm(x,y,1)
"""
if p<0: p=0
d = x - y
if p == 0:
return sum(d != 0)
elif p < 1:
return sum(abs(d)**p)
else:
return sum(abs(d)**p)**(1/p)
help(lp_norm)