Leverage the power of code recycling with functions. For interactive reading and executing code blocks and find b04-pyfun.ipynb, or install Python and JupyterLab locally.
Watch this section as a video
Watch this section as a video on the @Hydro-Morphodynamics channel on YouTube.
Quelles sont les fonctions?¶
Les fonctions sont un moyen pratique de diviser le code en blocs pratiques, réutilisables et plus lisibles, ce qui aide à structurer le code. Les blocs de fonctions peuvent accepter des arguments paramétriques et sont réutilisables. Ainsi, les fonctions sont un élément clé pour le partage du code et le travail en équipe. La structure de base d’une fonction Python implique:
Un mot-clé
defsuivi d’un nom de fonction avec arguments entre parenthèses et un bloc de code.Le type de arguments qu’une fonction peut recevoir sont :
arguments requis:
argParamètres de mots clés par défaut (avec valeurs par défaut) :
arg=valuearguments facultatifs:
*argsMot-clé facultatif :
**kwargs
L’utilisation d’arguments optionnels (mot clé) rend les fonctions plus robustes et flexibles. Le bloc de code d’une fonction est dentelé, similaire aux boucles:
def my_function(argument1, *args, **kwargs):
something = ... # do something with argument1, *args, and **kwargs
return somethingUn exemple de base¶
Seuls quelques pays (notamment les États-Unis) utilisent encore des unités impériales dans la vie quotidienne, tandis que la plupart des pays utilisent le système international d’unités* (SI). Écrivons une fonction simple pour aider les utilisateurs d’unités impériales à convertir pieds (impériaux) en mètres (SI).
Dans l’exemple suivant, le nom de la fonction est feet_to_meter et la fonction accepte un argument, qui est feet. La fonction renvoie l’argument feet multiplié par un conversion_factor de 0.3048, qui correspond au facteur de conversion des pieds aux mètres. Dans cet exemple simple, la variable conversion_factor ne peut être modifiée externement et n’existe que dans l’espace namespace de la fonction.
def feet_to_meter(feet):
conversion_factor = 0.3048
return conversion_factor * feetFonction Appels¶
Pour appeler une fonction, elle doit être définie avant l’appel. La fonction peut être définie dans le même script ou dans un autre script, qui peut ensuite être importé comme module (read more about modules and packages in the next section). Ensuite, nous pouvons appeler, par exemple, la fonction feet_to_meter définie ci-dessus comme suit:
feet_value = 10
print("{0} feet are {1} meters.".format(feet_value, feet_to_meter(feet_value)))10 feet are 3.048 meters.
Arguments facultatifs¶
Remplacer l’argument non facultatif feet dans la fonction ci-dessus par un argument facultatif *args permet de convertir autant de valeurs de longueur que la fonction reçoit. Les lignes suivantes expliquent étape par étape comment cela fonctionne.
Assurez-vous que quiconque comprend les paramètres d’entrée et de sortie de la fonction en ajoutant en ligne docstrings avec une paire de doubles apostrophes triples (
""") qui englobe les paramètres d’entrée (:params parameter_name: definition) et le retour de la fonction (:output: definition).Par défaut, nous supposerons que plusieurs valeurs sont fournies. Par conséquent, une liste appelée
value_listest instantanée au début de la fonction, tandis queconversion_factorreste la même qu’avant.A for-loop over
*argsidentifie et traite les arguments fournis. Pourquoi une boucle ?
Python collecte automatiquement*argsdans un tuple, et donc, nous pouvons itérer sur*args, même si les valeurs fournies n’ont pas été passées comme une liste ou un tuple.Dans le bloc de code
try, la zone de référence comprend un énoncétry-exceptpour vérifier si les valeurs fournies (arguments) sont numériques et peuvent être converties en compteurs. Si le bloctryfonctionne avec succès, l’expressionarg * conversion_factorajoute l’argument convertiargàvalue_list.Finalement, le mot-clé
returnretourne la liste de valeurs.
def feet_to_meter(*args):
"""
:param *args: numeric values in feet
:output: returns list of values in meter
"""
value_list = []
conversion_factor = 0.3048
for arg in args:
try:
value_list.append(arg * conversion_factor)
except TypeError:
print(str(arg) + " is not a number.")
return value_listAvec la fonction nouvellement définie et plus flexible, nous pouvons maintenant appeler feet_to_meter avec autant d’arguments que nécessaire:
print("Function call with 3 values: ")
print(feet_to_meter(3, 1, 10))
print("Function call with no value: ")
print(feet_to_meter())
print("Function call with non-numeric values:")
print(feet_to_meter("just", "words"))
print("Function call with mixed numeric and non-numeric values:")
print(feet_to_meter("just", "words", 2))Function call with 3 values:
[0.9144000000000001, 0.3048, 3.048]
Function call with no value:
[]
Function call with non-numeric values:
just is not a number.
words is not a number.
[]
Function call with mixed numeric and non-numeric values:
just is not a number.
words is not a number.
[0.6096]
Mot-clé optionnel Arguments **kwargs¶
Dans les derniers paragraphes, nous avons rendu la fonction feet_to_meter plus flexible afin qu’elle puisse maintenant recevoir autant d’arguments que nécessaire. Jusqu’à présent, la variable interne conversion_factor ne peut pas être modifiée de l’extérieur de la fonction, ce qui limite la flexibilité. Par exemple, imaginons que nous écrivions cette fonction pour un historien. Dans le passé, les unités impériales étaient répandues dans de nombreuses cultures (p. ex. grecques, romaines ou chinoises) avec des définitions de longueur variant entre 0,250 m et 0,335 m. Cela signifie que l’historien aura besoin de flexibilité en ce qui concerne le facteur de conversion, alors que nous voulons toujours utiliser 0.3048 m comme valeur par défaut. Cette exigence peut être implémentée avec des arguments optionnels **kwargs et c’est ainsi que cela fonctionne dans le bloc de code ci-dessous:
Ajouter
**kwargsaprès*argsdans la fonctiondefparenthèses (l’ordre de*args, **kwargsest important).Gardez
conversion_factor = 0.3048comme valeur par défaut (nous voulons que la fonction soit fonctionnelle aussi sans aucun argument de mot-clé fourni).Comme pour l’instruction
*args, Python identifie automatiquement les variables commençant par**comme des arguments optionnels (en fait, le nom args et kwargs n’a pas d’importance - les signes*sont importants). La différence à*argsest que Python identifie**kwargscomme un dictionnaire.A for-loop itrates over the kwargs-dictionary et l’instruction
ifidentifie tout paramètre de mot-clé optionnel qui contient la chaîne"conv"en tant que conversion factor.A
try-exceptstatement teste si la valeur fournie pour l’argument clé est numérique en tentant une conversion àfloat().
Le reste de la fonction reste inchangé.
def feet_to_meter(*args, **kwargs):
"""
:param *args: numeric values in feet
:output: returns list of values in meter
"""
value_list = []
conversion_factor = 0.3048
for key, value in kwargs.items():
if "conv" in key:
try:
conversion_factor = float(value)
print("Using conversion factor = " + str(value))
except (ValueError, TypeError):
print(str(value) + " is not a number (using default value 0.3048).")
for arg in args:
try:
value_list.append(arg * conversion_factor)
except TypeError:
print(str(arg) + " is not a number.")
return value_listTester différents facteurs de conversion avec la flexibilité nouvellement définie de la fonction feet_to_meter:
print("Function call with 3 values and a conversion factor of 0.25: ")
print(feet_to_meter(3, 1, 10, conv_factor=0.25))
print("Function call with 3 values and a conversion factor of 1/7 with slightly different name: ")
print(feet_to_meter(3, 1, 10, conversion_factor=1/7))
print("Function call with 2 values with default conversion factor: ")
print(feet_to_meter(25, 10))Function call with 3 values and a conversion factor of 0.25:
Using conversion factor = 0.25
[0.75, 0.25, 2.5]
Function call with 3 values and a conversion factor of 1/7 with slightly different name:
Using conversion factor = 0.14285714285714285
[0.42857142857142855, 0.14285714285714285, 1.4285714285714284]
Function call with 2 values with default conversion factor:
[7.62, 3.048]
Arguments de mots clés par défaut¶
Les arguments de mots clés peuvent également être définis par défaut. L’exemple ci-dessous montre comment le conversion_factor peut être défini par défaut dans les parenthèses de la fonction def. Notez que conversion_factor doit être défini après tout argument optionnel *args.
def feet_to_meter(*args, conversion_factor=0.3048):
"""
:param *args: numeric values in feet
:output: returns list of values in meter
"""
value_list = []
for arg in args:
try:
value_list.append(arg * conversion_factor)
except TypeError:
print(str(arg) + " is not a number.")
return value_listMaintenant nous pouvons utiliser feet_to_meter avec ou sans ou avec un facteur de conversion et après une liste de valeurs:
print("Function call with a conversion factor of 0.313 and two values: ")
print(feet_to_meter(1, 10, conversion_factor=0.313))
print("Function call with 3 values without any conversion factor: ")
print(feet_to_meter(3, 1, 10))Function call with a conversion factor of 0.313 and two values:
[0.313, 3.13]
Function call with 3 values without any conversion factor:
[0.9144000000000001, 0.3048, 3.048]
Function Wrappers and Decorators¶
Si plusieurs fonctions contiennent des lignes similaires, il est probable que ces fonctions peuvent être davantage factorisées en utilisant des enveloppes de fonction et des décorateurs. Un exemple typique est une commande de licence (par exemple pour utiliser un module/paquet Python commercial, comme le arcpy d’Esri) ou si nous voulons utiliser une déclaration d’erreur récurrente avec try - except des déclarations.
Par exemple, envisagez deux ou plusieurs fonctions qui devraient recevoir, traiter et produire la sortie numérique à partir de l’entrée de l’utilisateur. Ces fonctions peuvent ressembler à ceci :
def multiply_arguments(*args):
result = 1.0
try:
for arg in args:
result *= arg
print("The result is: " + str(result))
except TypeError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
except ValueError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
return result
def sum_up_arguments(*args):
result = 0.0
try:
for arg in args:
result += arg
except TypeError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
except ValueError:
print("ERROR: The calculation could not be performed (input arguments: %s)" % str(args))
return resultLes deux fonctions impliquent l’instruction print("The result is: " + str(result)) pour imprimer les résultats à la console Python (p. ex., pour obtenir des informations intermédiaires) et pour exécuter uniquement sur des entrées valides (c.-à-d. numériques) à l’aide d’une exception (try - except). Cependant, nous voulons que nos fonctions se concentrent uniquement sur le calcul et c’est là qu’une fonction d’emballage aide.
Une fonction d’emballage peut être définie en définissant d’abord une fonction standard (par exemple, def verify_result) puis en passant une autre fonction (func) comme argument. Dans cette fonction (verify_result), nous pouvons alors placer une fonction imbriquée def wrapper() qui embrassera func. Il est important d’utiliser le mot-clé optionnel *args et le mot-clé optionnel **kwargs dans la fonction d’emballage et d’appeler func pour rendre l’emballage aussi flexible que possible.
def verify_result(func):
def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
print("Success. The result is %1.3f." % float(result))
return result
except TypeError:
print("ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: %s)" % str(args))
return 0.0
except ValueError:
print("ERROR: The calculation could not be performed because of non-numeric input (input arguments: %s)" % str(args))
return 0.0
return wrapperMaintenant, nous pouvons utiliser un @-decorator pour envelopper les fonctions de mathématiques ci-dessus dans la fonction verify_result(fun). Lorsque Python lit le beau signe @, il recherche automatiquement la fonction d’emballage définie après le signe @ pour envelopper la fonction suivante.
@verify_result
def multiply_arguments(*args):
result = 1.0
for arg in args:
result *= arg
return result
@verify_result
def sum_up_arguments(*args):
result = 0.0
for arg in args:
result += arg
return resultLes deux fonctions (multiply_arguments et sum_up_arguments) peuvent être appelées comme d’habitude, par exemple :
multiply_arguments(3, 4)
multiply_arguments(3, 4, "not a number")
sum_up_arguments(3, 4)
sum_up_arguments("absolutely", "no", "valid", "input")Success. The result is 12.000.
ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: (3, 4, 'not a number'))
Success. The result is 7.000.
ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: ('absolutely', 'no', 'valid', 'input'))
0.0La fonction wrapper ci-dessus renvoie aussi les résultats de la fonction enveloppée. Cependant, pour utiliser des attributs de fonction intégrés (par exemple, le nom de la fonction avec __name__, le docstring de la fonction avec __doc__, ou le module dans lequel la fonction est définie avec __module__) en dehors de l’emballage, nous avons besoin de la fonction d’emballage pour retourner la fonction enveloppée (décorée) elle-même. Cela peut se faire comme suit:
def error_func(*args, **kwargs):
return 0.0
def verify_result(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except TypeError:
print("ERROR: The calculation could not be performed because of at least one non-numeric input (input arguments: %s)" % str(args))
return error_func(*args, **kwargs)
except ValueError:
print("ERROR: The calculation could not be performed because of non-numeric input (input arguments: %s)" % str(args))
return error_func(*args, **kwargs)
return wrapperNotez la différence : la fonction wrapper retourne maintenant func(*arg, **kwargs) au lieu des variables numériques en conséquence. Si la fonction ne peut pas être exécutée à cause d’une entrée non valide, l’emballage retourne une fonction d’erreur (error_func), ce qui assure la cohérence de la fonction d’emballage. On peut penser que la fonction d’erreur renvoyant 0.0 est obsolète car les instructions d’exception pourraient directement retourner 0.0. Cependant, 0.0 est une variable float, alors que error_func est une fonction et que l’emballage de la fonction doit toujours retourner le même type de données, indépendamment d’une augmentation d’exception (erreur) ou d’une exécution réussie. C’est ce qui rend le code cohérent.
Ce paragraphe montre des exemples d’utilisation des décorateurs sous la forme d’un signe @ pour envelopper (embrace) une fonction. Les décorateurs sont également une fonctionnalité utile dans les classes Python, par exemple, quand une fonction de classe retourne des valeurs statiques. En savoir plus sur les décorateurs dans les cours plus tard dans le chapitre sur object orientation and classes.
Itérateurs et générateurs¶
Une caractéristique des types de données list, tuple et dictionnaire est leur itérabilité, qui est fournie par leur méthode intégrée __iter__. Ainsi, l’itérérabilité est la raison pour laquelle nous pouvons écrire:
for e in [1, 2, 3]: print(e)1
2
3
Outre les itérations, Python permet également la création de générateurs (c.-à-d. fonctions de générateur). Au lieu d’utiliser une déclaration return, une fonction de générateur se termine par une déclaration yield, qui retourne les données tant qu’une fonction next() (étape inhérente dans les itérations) est appelée. Une application d’un générateur est, par exemple, l’aplatissement des listes imbriquées (c.-à-d., supprimer les sous-listes et écrire toutes les variables directement dans une liste non-négative) :
from collections.abc import Iterable
def flatten(nested_list):
for e in nested_list:
if isinstance(e, Iterable) and not isinstance(e, str):
for x in flatten(e):
yield x
else:
yield e
a_nested_list = [[1, 2, 3], ["a", "b", "c"]]
flattened_list = list(flatten(a_nested_list))
print(flattened_list)[1, 2, 3, 'a', 'b', 'c']
Fonctions Lambda¶
Lambda (λ) calcul est un langage formel pour exprimer l’abstraction de fonction basée sur le calcul et a été introduit dans les années 1930 par l’église Alonzo mathématicien. Les fonctions Lambda proviennent de la programmation fonctionnelle et représentent des fonctions courtes et anonymes (sans nom). Bien que le Python ne soit pas intrinsèquement un langage de programmation fonctionnelle, des concepts fonctionnels ont été mis en œuvre au début du Python, par exemple avec les fonctions map(), filter() et reduce() et aussi l’opérateur lambda.
Dans Python, une fonction anonyme (sans nom) lambda peut prendre n’importe quel nombre d’arguments, mais ne peut avoir qu’une seule expression. Les arguments consistent en une liste de variables séparées par des virgules et l’expression utilise ces arguments. Les fonctions syntax de lambda sont :
lambda arguments : expression
L’exemple suivant illustre une fonction lambda avec un seul argument et ajoute 1 à l’argument :
add_one = lambda number : number + 1
print(add_one(1))2
C’était gentil mais tout à fait inutile. Voici un exemple d’une fonction lambda un peu plus utile qui résume deux arguments d’entrée:
sum_up = lambda x, y : x + y
print(sum_up(1, 5))6
La fonction ci-dessus montrée pour convertir les pieds en mètres peut également être écrite comme une fonction lambda:
feet_to_meter = lambda ft_value : ft_value * 0.3048
print(feet_to_meter(10))3.048
Grâce à une fonction lambda, le code est plus court et plus efficace. De plus, pour évaluer la fonction feet_to_meter lambda pour plusieurs valeurs, nous pouvons utiliser la fonction map(). La syntaxe d’une fonction map() est :
result = map(function, sequence)
où sequence peut être une list ou un tuple. Ainsi, pour évaluer un tuple de quatre valeurs, nous pouvons écrire:
four_ft_values = (4, 9.7, 7, 2)
print(list(map(feet_to_meter, four_ft_values)))[1.2192, 2.95656, 2.1336, 0.6096]
La fonction list() convertit la sortie map() en une list pour évaluer la fonction map() (autrement, le résultat serait quelque chose comme <map object at ...>).
Si la fonction feet_to_meter n’est pas nécessaire à un autre endroit du code, on peut aussi écrire :
print(list(map(lambda x : x * 0.3048, (4, 9.7, 7, 2))))[1.2192, 2.95656, 2.1336, 0.6096]
Une autre caractéristique de Python est la fonction filter(function, list) qui représente une solution élégante pour filtrer ces éléments d’une liste pour laquelle la fonction retourne True. Le bloc de code suivant illustre un filter qui élimine tous les numéros d’une liste some_numbers, qui peut être divisée par trois.
some_numbers = list(range(1, 10))
print(list(filter(lambda x: x % 3, some_numbers)))[1, 2, 4, 5, 7, 8]
Auparavant, la fonction reduce() pour fusionner l’entrée de liste vers le bas en une seule valeur a été implémentée dans Python. Cependant, l’auteur original de Python Guido van Rossum l’a retiré de l’espace de noms intégré dans Python 3 — il vit maintenant à functools.reduce (lire son post), ce qui explique pourquoi il n’est pas présenté ici comme un élément intégré.
Vérification de la réussite en apprentissage¶
Prenez le test de réussite d’apprentissage pour ce carnet Jupyter.
Unfold QR Code
