<jupyter_start><jupyter_text>Partie 4 : langage procédural  Python: un langage procédural Lorsque les programmes se complexifient, la longueur du code augmente et les traitements s'enchaînent, en utilisant souvent les mêmes sous-traitements élémentaires (tri, recherche...) à un paramètre près.Le principe de la programmation procédurale est de regrouper ces sous-traitements (appelés aussi sous-routines, procédures, ou fonctions -- à différencier de la notion mathématique) et de les nommer afin de pouvoir les appeler à n'importe quelle étape du programme, y compris dans d'autre procédures, voire, dans le corps même de la procédure (on parle alors de récursivité).Un tel type de programmation possède par ailleurs les avantages suivants :- il est possible de réutiliser le même code à différents emplacements dans le programme (c'est ce qu'on appelle la "factorisation" du code). Cela a pour effet à la fois de réduire la taille du code mais aussi de localiser les modifications, ce qui facilite la maintenabilité (compréhension plus rapide, réduction du risque de régression) ;- il est plus facile de suivre l'exécution du programme  FonctionsL'unité procédurale de base en Python est la "fonction" : elle regroupe un ensemble de traitements locaux et renvoie une valeur à l'issue du traitement. Ce n'est pas une fonction au sens mathématique à proprement parler, car elle peut avoir des "effets de bords". Les variables définies dans la fonctions ne sont pas visibles en dehors de celle-ci.On déclare les fonctions grâce au mot-clef `def`.    def name(param):        statement        return val<jupyter_code>def hello():
    print("Hello")
    return True
result=hello()
result<jupyter_output>Hello<jupyter_text>Une fonction qui ne renvoie rien... renvoie `None` !<jupyter_code>def affiche():
    print("Salut")
    return None # Cette ligne est facultative
    
res=affiche()
type(res)<jupyter_output>Salut<jupyter_text>Paramètres Afin d'être réutilisables, les fonctions sont paramétrées.Le cas le plus courant est le suivant :<jupyter_code>def course(name, year="2019"):
    print("Nous sommes en ",year)
    print("Vous êtes dans le cours",name)

course("NSI")<jupyter_output>Nous sommes en  2019
Vous êtes dans le cours NSI<jupyter_text>> **Exercice : ecrire une fonction `polynome(x,coeff)` qui calcule la valeur en $x$ du polynome dont les coefficients sont passés en paramêtre (ex : [1,2,3] correspond au polynôme $x^2+2x+3$** Il est aussi possible de définir une fonction avec des paramètres variables (mais c'est beaucoup plus difficile à maintenir !).<jupyter_code>def cours(*args):
    print("Vous etes en ",args[0])
    print("Vous suivez le cours de ",args[1])
cours("DU","NSI")<jupyter_output>Vous etes en  DU
Vous suivez le cours de  NSI<jupyter_text>Enfin, il est possible de définir les paramètres comme des dictionnaires :<jupyter_code>def cours(**args):
    print("Vous etes en ",args["annee"])
    print("Vous suivez le cours de ",args["nom"])

cours(nom="NSI",annee="DU")
cours(annee="DU",nom="NSI")<jupyter_output>Vous etes en  DU
Vous suivez le cours de  NSI
Vous etes en  DU
Vous suivez le cours de  NSI<jupyter_text>Fonctions récursivesUne fonction récursive est simplement une fonction qui s'appelle elle-même.<jupyter_code>def fact(n):
    if (n<=0): return 1
    else: return n*fact(n-1)
    
fact(3)<jupyter_output><empty_output><jupyter_text>> **Exercice : ecrire la fonction fibo(n) qui calcule le nème terme de la suite de Fibonnaci : $U_0=U_1=1$ ; $U_n=U_{n-1}+U_{n-2}$**  Fonctions lambdaOn peut associer une fonction non nommée à une variable.<jupyter_code>fonc=lambda x,y:x**2+2*x*y+y**2
fonc(2,2)<jupyter_output><empty_output><jupyter_text>> **Exercice : ecrire une fonction `somme(f,l)` qui calcule la somme desx valeur de la fonction $f()$ (passée en paramêtre) pour tous les éléments de la liste l. Test en passant la fonction lambda qui calcule le carré d'un nombre**  Les modules PythonLorsque les programmes deviennent vraiment très gros, il est nécessaire de les organiser par groupes de fonctionnalités indépendantes (principe du ["separation of concerns"](https://en.wikipedia.org/wiki/Separation_of_concerns)). Un programme qui intègre ces principes est appelé un programme modulaire, car il sépare son code en différent modules. La modularité, et donc la séparation des préoccupations, est obtenue en encapsulant des informations dans une section de code qui a une interface bien définie. L’encapsulation du code dans des modules amène un masquage de l’information. L’application du principe de séparation des préoccupations simplifie le développement et la maintenance des programmes informatiques. Quand les préoccupations sont strictement séparées, les différentes parties du code peuvent être réutilisées, étendues ou modifiées indépendamment des autres. Cela permet ainsi d’intervenir sur une partie du code sans avoir de connaissance particulière sur l’ensemble des autres parties. En Python un module est simplement un fichier `.py` regroupant plusieurs fonctions ou traitements.On peut accéder aux fonctions et variables définies dans le module en "important" le module.    Soit `mymodule.py` le nom du module qui contient :<jupyter_code>def square(x,y):
    return x**2 + 2*x*y + y**2<jupyter_output><empty_output><jupyter_text>Pour appeler la fonction `square`, on doit importer le module qui la contient`import mymodule``monmodule.square(2,2)``16`  DocumentationLa documentation en python est très simple à mettre en oeuvre et il ne faut donc surtout pas s'en priver !<jupyter_code># mymodule.py
"""
Square module
"""
def square(x,y):
    """
    Square function
    
    Computes x^2+2xy+y^2
    """
    return x**2 + 2*x*y + y**2<jupyter_output><empty_output><jupyter_text>>>> help(mymodule)    Help on module mymodule:    NAME        mymodule - Module square    FILE       /home/revel/Cours/Python/mymodule.py    FUNCTIONS       square(x, y)           Square function           Calcule x^2+2xy+y^2 On peut accéder simplement à la documentation en tappant `help(cmd)`.  Quelques fonctions et modules utilesNous allons voir différents package usuels.  Le package `__builtin__`C'est le package importé par défaut (pas besoin donc de le faire). Il comporte de nombreuses fonctions permettant de manipuler les types courants.  Fonctions d'affichage et de lecture<jupyter_code>print("Salut","comment","ça","va ?")
a=input("Entrez une valeur :")
print("A=",a)<jupyter_output>Entrez une valeur :10
A= 10<jupyter_text>Fonctions de conversion de types<jupyter_code>bool(0), bool(10) # bool(x) > True si x !=0
int(2.3)
float(10)
chr(65) # Charactère correspondant au code ASCII
ord('A') # Code ASCII de...
str(12.0)
complex(1,2)<jupyter_output><empty_output><jupyter_text>Fonctions de conversion de base<jupyter_code>bin(20)
oct(20)
hex(20)<jupyter_output><empty_output><jupyter_text>Fonctions numériques<jupyter_code>abs(-1) # Valeur absolue
abs(1+1j) # Module
divmod(5,2) # division entière et reste
pow(2.0,3.0)
round(1/3,2)<jupyter_output><empty_output><jupyter_text>Les fonctions de gestion de fichiersAfin de pouvoir sauvegarder et récupérer des données traitées par un programme, il faut pouvoir manipuler des fichiers.<jupyter_code>with open("./sortie.txt","w") as fichier: # Ouverture d'un fichier en écriture 'w' - write
    fichier.write("Test d'écriture\n")
    fichier.write("Ligne 2")
    
with open("./sortie.txt") as fichier:
    for i in fichier:
        print(i)<jupyter_output>Test d'écriture

Ligne 2<jupyter_text>Les fonctions s'appliquant sur des conteneurs<jupyter_code>len([1,2,3,4])
max([1,2,3,4]),min([1,2,3,4]),sum([1,2,3,4])
sorted([4,2,1,3]) # Tri 
list(zip(range(3),["A","B","C"])) # Association de 2 séquences de même taille
list(enumerate(["A","B","C"])) # Numérote les éléments d'une séquence
list(map(lambda x:x**2,(1,2,3))) # Application d'une fonction à tous les éléments d'un conteneur
list(filter(lambda x:x>0,[-1,2,-3,4]))
all([1,1,1,1]),all([False,True,True,False]),all([0,0,0,0]) # Teste si toutes les valeurs sont vraies
any([1,1,1,1]),any([False,True,True,False]),any([0,0,0,0]) # Teste si au moins une valeurs est vraie<jupyter_output><empty_output><jupyter_text>> **Exercice : trouver la position du max dans une liste (utiliser enumerate et max bien paramétré)**  Les fonctions sur les chaînes<jupyter_code>"Comment allez vous ?".split()
"Salut {1} {0}".format(1,2)
"Salut les copains".startswith("Salut"),"Salut les copains".endswith("amis")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10168691754158311, 'fraction_numerical': 0.016161377845936062, 'mean_word_length': 5.561919504643963, 'pattern_counts': {'":': 0, '<': 59, '<?xml version=': 0, '>': 67, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '279314', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2981, 'n_tokens_neox': 2958, 'n_words': 963, 'path': '4-Python_programmation_procedurale.ipynb', 'repo_name': 'inefe87-17/conda', 'size': '8477'}