Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Python : fonctions perso. complexes & modules perso.

Retour sur les fonctions

J'ai déjà évoqué la création de fonctions personnelles dans le chapitre 4. Mais je n'ai pas tout dit car le sujet est vaste.

Fonctions avec un nombre fixe d'arguments

Que se passe t-il si on ne passe pas le nombre correct d'arguments ?

>>> pow(10,5)
100000
>>> pow(10)
...
TypeError: pow() missing required argument 'exp' (pos 2)

La fonction native pow() exige obligatoirement deux arguments lors de son appel. Donc si n'en passe qu'un argument, il y a plantage !

Fonction avec argument optionnel

Je vous montre maintenant que l'on peut donner une valeur par défaut à certains paramètres.
Aussi lors de l'appel de la fonction je ne suis pas obligé de saisir un argument correspondant au paramètre ayant une valeur par défaut.

>>> def puissance(x, p =2) :
...     return pow(x,p)
...
>>> puissance(5)
25  # 5 à la puissance 2
>>> puissance(5,3)
125
>>>

J'ai créé une fonction personnelle nommée puissance() et qui est une amélioration de la fonction native pow().
Notez la syntaxe : def puissance(x, p =2).
Lors de l'appel de la fonction je peux passer un seul argument ; le paramètre "p" prendra alors la valeur 2 : élever à la puissance 2. Mais bien sûr je peux aussi passer deux arguments si je veux une puissance différente de 2.

Fonction flexible

Notion de fonction flexible

Certaines fonctions prédéfinies de Python acceptent un nombre variable d'arguments. On peut parler de fonctions flexibles.
C'est le cas de la fonction math.gcd() qui précise le plus grand commun diviseur à plusieurs nombres.

>>> import math
>>> math.gcd(50,30)
10
>>> math.gcd(30,45,60)
15
>>>

Comment créer une fonction personnelle flexible ?

La fonction native sum() doit être argumentée avec un argument unique correspondant à une liste.

>>> maliste = range(10)
>>> sum(maliste)
45
>>> sum(1,2,3,4)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: sum() takes at most 2 arguments (4 given)

Je crée une fonction personnelle plus souple d'emploi que je nomme : somme().

 
>>> def somme(*args):
...     s=0
...     for i in args:
...             s = s+i
...     return s
...
>>> somme(10,20)
30
>>> somme(10,20,15,25)
70
>>>

Lors de la déclaration de la fonction le paramètre doit être "* args" (remplacez 'args' par ce que voulez mais surtout n'oubliez pas le caractère "*" devant le mot retenu).
Lors de l'appel de la fonction, vous saisissez en guise d'arguments un tuple de valeurs avec 2,3,4, ... n éléments.

Fonction récursive

Récursivité, un mot qui fait peur aux apprentis programmeurs.
Vous devez savoir qu’une fonction peut également s’appeler elle même dans son exécution. C’est ce qu’on appelle la récursivité.
L’exemple classique de fonction récursive est celle qui calcule une factorielle.
La factorielle de 4, par exemple, est égale à 1 * 2 * 3 * 4 = 24
La factorielle de 6 = 1 * 2 * 3 * 4 * 5 * 6 = 720

Fonction récursive pour calculer factorielle n

def factorielle(n):
    if n == 0:
        return 1
    else:
        return n  * factorielle(n-1)

Dans la fonction notez la syntaxe : return n * factorielle(n-1) : appel récursif.
Si on argumente avec n > à 1 on retourne cette valeur et on appelle factorielle(n-1).
Si n-1 est toujours > à 1, on retourne cette valeur et on appelle à nouveau notre fonction avec une valeur diminuée de 1 ce jusqu’à ce que la valeur passée à factorielle() soit 1.

Programme sans récursivité

On peut éviter la récursivité à condition d'utiliser la fonction range().

# nom_prog : factoriel_range.py
def calcul_factoriel(nbre) : 
    factoriel = 1
    for i in range(1, nbre+1):
        factoriel = factoriel * i
    return factoriel

print(calcul_factoriel(3))
print(calcul_factoriel(4))
nbre = int(input('saisir un entier : '))
print(calcul_factoriel(nbre))

Dans ce programme j'appelle trois fois la fonction perso. "calcul_factoriel()".

Le rendu :

6
24
saisir un entier : 5
120
>>> 

Solution ultime

Nous nous serions évités tous ces efforts de programmation si nous avions pris la peine de rechercher dans la documentation Python.
Ce langage est très orienté "mathématiques" (c'est pour cette raison qu'il est enseigné en lycée dans les filières Math sup & Math spé.
Il existe dans le module math la fonction factorial()

>>> import math
>>> math.factorial(4)
24
>>> math.factorial(6)
720

Une fonction définie en une seule ligne

Une fonction peut être définie en une seule instruction à condition d'utiliser le mot clé "lamda".
Aussi désigne t-on ces fonctions sous l'expression "fonctions lambda".
Dans une fonction lambda on ne retrouve pas les mots clés "def" & "return".

>>> carre = lambda x: x*x
>>> carre(5)
25
>>> carre(10)
100
>>>

La syntaxe d'une fonction lambda : nomFonction = lambda arg1, arg2 : instruction de retour
J'ai défini la fonction "carre".
Concernant l'appel de la fonction il n'y a pas de différence avec une fonction classique : nomFonction(liste des arguments)

La fonction map()

Dans la liste des fonctions "built in" de Python il existe la fonction map().

Cette fonction native permet d'appliquer une fonction à chaque élément d'un itérable et de retourner un nouvel itérable avec les résultats, sans utiliser de boucle for.
La fonction map() peut être utilisée conjointement avec une fonction "lambda".

>>> maliste = [3,5,7]
>>> carres = map(lambda x:x*x,maliste)
>>> type(carres)
class 'map'
>>> carres
map object at 0x000001FD7F8646D0
>>> resultats = list(carres)
>>> resultats
[9, 25, 49]

La fonction lambda qui calcule le carré est appliquée à tous les termes de "maliste" grâce à la fonction map().
Pour obtenir la liste des carrés (nommée "resultats") il faut appliquer la méthode list() à l'objet "carres".

Grâce à la fonction native map() j'ai donc appelé N fois la même fonction sans devoir recourir à une boucle.

Créer ses propres modules

Pourquoi créer ses propres modules ?

Vous avez du créer une fonction personnelles car vous n'avez pas trouvé votre bonheur dans le module de base, les modules préinstallés, les bibliothèques. Mais avez-vous vraiment bien cherché ?

Si vous créer cette fonction dans le cadre d'un programme, celle-ci ne pourra être appelée qu'à partir de ce programme.
Ce qui est vraiment dommage cette fonction est vraiment innovante et représente un gros investissement personnel.
Par contre une fonction appartenant à un module peut être appelée via la console ou un programme.

Comment créer un module perso. ?

Je crée un fichier nommé "geometrie.py" (comme un programme).

Contenu de ce fichier

""" Module geometrie
qui comprend deux fonctions : cercle() et rectangle() : 
- cercle(rayon) -> circonférence, aire
- rectangle(largeur,longueur) -> périmètre, aire
"""

def cercle(rayon):
    circonference = rayon * 2 * 3.14
    aire = rayon* rayon * 3.14
    return circonference, aire

def rectangle(longueur,largeur):
    perimetre = (longueur *2) + (largeur*2)
    aire = longueur*largeur 
    return perimetre, aire

Un module perso. doit débuter par un commentaire multilignes (délimité par une paire de triple guillemets). Cette chaine de commentaire sera affichée via la commande help().
Il faut donc soigner la rédaction de ce texte. Elle est appelée "docstrings" dans la documentation de Python.
Ensuite on trouve la définition des différentes fonctions et c'est tout !

Pour devenir un module à part entière, le fichier "geometrie.py" doit être enregistré dans le répertoire courant de Python.

Comment connaitre le répertoire courant pour Python ?

La procédure :

>>> import sys
>>> sys.path
['', 'C:\\Users\\darch\\AppData\\Local\\Programs\\Python\\Python313\\python313.zip', 
'C:\\Users\\darch\\AppData\\Local\\Programs\\Python\\Python313\\DLLs', 
'C:\\Users\\darch\\AppData\\Local\\Programs\\Python\\Python313\\Lib', 
'C:\\Users\\darch\\AppData\\Local\\Programs\\Python\\Python313',
'C:\\Users\\darch\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages']

Tant que j'utilisais la version 3.9 de Python le répertoire courant était "c:/Python39" , comme je l'indique encore dans certaines pages de ce tuto.
Mais depuis que j'ai changé de PC et installé la version 3.13 de Python le répertoire courant à changé.

Utilisez la procédure ci-dessus pour connaitre le répertoire courant pour python.

Utilisation des fonctions du module perso "geometrie"

Le fichier "geometrie.py" est stocké (comme les autres modules) dans :
'C:\\Users\\darch\\AppData\\Local\\Programs\\Python\\Python313\\Lib'

L'appel des fonctions en mode commande

>>> import geometrie as geom
>>> import geometrie as geom
>>> help(geom)
Help on module geometrie:

NAME
    geometrie
MODULE REFERENCE
    https://docs.python.org/3.13/library/geometrie.html
...
DESCRIPTION
    Module geometrie
    qui comprend deux fonctions : cercle() et rectangle() :
    - cercle(rayon) -> circonférence, aire
    - rectangle(largeur,longueur) -> périmètre, aire
FUNCTIONS
    cercle(rayon)
    rectangle(longueur, largeur)
FILE
    c:\users\darch\appdata\local\programs\python\python313\lib\geometrie.py
>>> # appels des fonctions du module 
>>> geom.cercle(50)
(314.0, 7850.0)
>>> geom.rectangle(30,50)
(160, 1500)

Je sais que la fonction cercle() réclame deux arguments et la fonction rectangle() a besoin de deux arguments.
Chacune de ces fonctions retournent un tuple avec deux valeurs.

Utilisation du module perso dans le cadre d'un programme

Le programme
# module_perso_emploi.py
import geometrie as geom
import re
# expression régulière
format = "^[0-9]{1,}$"

# boucle de saisie 
valide = False
while not valide : 
    rayon = input("Valeur du rayon sous forme d'un entier :  ")
    largeur = input("Largeur du rectangle sous forme d'un entier : ")
    longueur = input("Longueur du rectangle sous forme d'un entier : ")
    if (re.search(format,rayon)) and (re.search(format,largeur)) and (re.search(format,longueur)):
        valide = True
        print ("saisies correctes")
    else :
        print("erreurs de saisie")

# conversions en numériques entiers
rayon = int(rayon)
largeur = int(largeur)
longueur = int(longueur)

# appel des fonctions 
circonference, aire = geom.cercle(rayon)
perimetre, surface = geom.rectangle(largeur, longueur)

# impressions
print(f"Le cercle de rayon {rayon} a une circonference de {circonference} et une aire de {aire}")
print(f"Le rect. de côtés {largeur} par {longueur} a un périmètre de {perimetre} et une surface de {surface}")

Il y a un contrôle de saisie : tant qu'on ne saisit pas 3 valeurs entières on reste dans la boucle de saisie.
Chaque saisie comparée à un format de saisie (une expression régulière).

N'oubliez pas les conversions. Il n'y jamais de conversions automatiques en Python à la différence de JS.

Appel des fonctions : celles-ci retournent des tuples !

Impression : j'utilise des "litteral strings".

Le rendu
Valeur du rayon sous forme d'un entier :  50
Largeur du rectangle sous forme d'un entier : 40
Longueur du rectangle sous forme d'un entier : 5 mètres
erreurs de saisie
Valeur du rayon sous forme d'un entier :  50
Largeur du rectangle sous forme d'un entier : 30
Longueur du rectangle sous forme d'un entier : 40
saisies correctes
Le cercle de rayon 50 a une circonference de 314.0 et une aire de 7850.0
Le rect. de côtés 30 par 40 a un périmètre de 140 et une surface de 1200

Le contrôle de saisie est efficace.