# Python

Toutes les documentations concernant le langage du serpent.

# [Python] Kit de base

## Introduction

Python est un langage interprété très permissif et souple.

Il supporte une multitude de modules rendant le langage ultra puissant pour divers types d'utilisations.

[![image.png](https://wiki.neopipe.fr/uploads/images/gallery/2023-09/scaled-1680-/4pVimage.png)](https://wiki.neopipe.fr/uploads/images/gallery/2023-09/4pVimage.png)

## Variables

- Entier

```python
#Affectation d'un entier
myVar=3
```

- Flottant

```python
#Affectation d'un flottant
myVar=2.8
```

- Chaîne de caractères

```python
#Affectation d'une chaîne de caractères
myVar="Hello world" 
```

- Liste

```python
#Affectation d'une liste
myVar=[1, 2, 3]
```

- Dictionnaire

```python
#Affectation d'un dictionnaire
myVar={"clé1": "valeur1", "clé2": "valeur2"}
```

- Tuple (liste de constantes) :

```python
#Affectation d'un tuple
myVar = (1, 2, 3, 4, 5)
```

- Set (liste sans doublon) :

```python
#Affectation d'un set
myVar = {1, 2, 3, 4, 5}
```

- Booléen

```python
#Affectation d'un booléen
myVar=True
```

## Print

Pour afficher une variable dans la console :

```python
print(myVar)
```

Ou la version formatée :

```python
print(f"Hello {name} !")
```

## Input

Il peut être intéressant de récupérer une saisie de la part de l'utilisateur.

Dans ce cas, la fonction input le permet :

```python
entree_utilisateur = input("Entrez votre nom : ")
```

<span style="text-decoration: underline;">Remarque :</span> La fonction renvoie une chaîne de caractère.

Il peut donc être intéressant de transtyper la valeur de retour.

## Transtypage (casting)

L'intérêt du **transtypage** est de passer d'un type de variable à un autre (dans la mesure du possible).

Cela peut s'avérer utile pour transformer une chaîne de caractères saisie par l'utilisateur et la transformer en type entier afin de pouvoir effectuer des calculs par exemple.

On peut aussi s'en servir pour convertir un **float** en **int** et ne récupérer ainsi que la partie entière d'un nombre.

En reprenant l'exemple de l'entrée utilisateur ci-dessus, voici comment transformer l'input **string** vers **int** :

```python
nombre_entier = int(entree_utilisateur)
```

La variable **nombre\_entier** pourra ensuite être traitée dans un algorithme de calcul.

## Commentaires

En Python, il existe 2 types de commentaires :

- Commentaire sur une seule ligne :  
    ```python
    # Voici un commentaire sur une seule ligne
    ```
- Commentaire sur plusieurs lignes :  
    ```python
    """
    Voici un commentaire
    sur plusieurs
    lignes
    """
    ```

## Opérations mathématiques

```python
a = 10
b = 5

addition = a + b           # Addition
soustraction = a - b       # Soustraction
multiplication = a * b     # Multiplication
division = a / b           # Division
modulo = a % b             # Modulo (reste de la division entière)
exposant = a ** b          # Exposant
```

## Conditions

Il est possible de réaliser certaines instructions seulement sous certaines conditions.

Pour cela on utilise les structures conditionnelles.

Ces structures conditionnelles vérifient des conditions grâce aux opérateurs conditionnels.

#### Opérateurs conditionnels

<table border="1" id="bkmrk-op%C3%A9rateur-conditionn" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><tbody><tr><td class="align-center">**Opérateur conditionnels**</td><td class="align-center">**Description**</td></tr><tr><td class="align-center">==</td><td class="align-center">Égalité</td></tr><tr><td class="align-center">!=</td><td class="align-center">Différence</td></tr><tr><td class="align-center">&gt;</td><td class="align-center">Supérieur strict</td></tr><tr><td class="align-center">&lt;</td><td class="align-center">Inférieur strict</td></tr><tr><td class="align-center">&gt;=</td><td class="align-center">Supérieur ou égal</td></tr><tr><td class="align-center">&lt;=</td><td class="align-center">Inférieur ou égal</td></tr></tbody></table>

#### If

La structure conditionnelle la plus simple est le **if** . Voici sa syntaxe :

```python
if a > b:
    print("a est plus grand que b")
```

<span style="text-decoration: underline;">Remarque :</span> Attention aux tabulations qu'il faut placer dans le bloc d'instructions où la condition est vérifiée.

#### If / Else

Le **if / else** reprend la même syntaxe en ajoutant le cas où la condition n'est pas respectée :

```python
if password == "p@ssW0rD":
    print("Mot de passe correct")
else:
    print("Mot de passe incorrect")
```

#### If / Elif / Else

Le **if / elif / else** reprend la même syntaxe en ajoutant une nouvelle condition :

```python
if a > b:
    print("a est plus grand que b")
elif a < b:
    print("a est plus petit que b")
else:
    print("a est égal à b")
```

#### Match / case :  


Le **match / case** est semblable au switch case que l'on peut retrouver en langage C avec un syntaxe adaptée à Python.

Cette structure conditionnelle permet de tester uniquement des égalités :

```python
match a:
  case 'coucou':
    print("Hello")
  case : 'a plus':
    print("Au revoir")
```

Dans l'exemple ci-dessus, on teste d'abord le cas où si la variable a=='coucou' (si oui, on affiche "Hello").

Dans un deuxième temps, on teste le cas où si la variable a=='a plus' (si oui, on affiche "Au revoir").

## Boucles

#### While

La boucle **while** ou *tant que*, permet d'exécuter un bloc d'instructions tant qu'une condition est vérifiée :

```python
while a > 0:
    print(a)
    a = a - 1
```

#### For

La boucle for est plus puissante et peut être utilisée de différentes manières.

- Range : de 0 à X (ex: 5) ```python
    for i in range(5):
      print(i)
    ```
- Liste : Affiche les éléments d'une liste ```python
    for item in list:
      print(item)
    ```
- Liste avec incrément : Affiche les éléments d'une liste en conservant l'incrément ```python
    for i, item in enumerate(list):
      print(f"ID n°{i} --> {item}"
    ```

## Modules

Avec Python, il est possible d'utiliser des modules qui sont des bibliothèques ajoutant des fonctionnalités supplémentaires.

Pour installer un module, il suffit d'utiliser **pip** avec la commande suivante :

```bash
python -m pip install <MODULE>
```

Ainsi, le module sera présent sur votre système mais pas utilisable dans votre code.

Pour cela, il faut importer le module à la première ligne de votre code grâce à cette instruction :

```python
import <MODULE>
```

La syntaxe suivante permet d'importer seulement une ou plusieurs classes spécifiques d'un module :

```python
from <MODULE> import <CLASS>, <FUNCTION>
```

#### Liste des modules les plus courants

<table border="1" id="bkmrk-modules-description-" style="border-collapse: collapse; width: 100%; height: 273.766px;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><tbody><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">**Modules**</td><td class="align-center" style="height: 29.7969px;">**Description**</td></tr><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">time</td><td class="align-left" style="height: 29.7969px;">Gestion du temps et des dates.</td></tr><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">os</td><td class="align-left" style="height: 29.7969px;">Diverses interfaces pour le système d'exploitation.</td></tr><tr style="height: 35.3906px;"><td class="align-center" style="height: 35.3906px;">sys</td><td class="align-left" style="height: 35.3906px;">Paramètres et fonctions propres à des systèmes.

</td></tr><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">requests</td><td class="align-left" style="height: 29.7969px;">Utilisation des requêtes</td></tr><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">matplotlib</td><td class="align-left" style="height: 29.7969px;">Création de graphique.</td></tr><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">numpy</td><td class="align-left" style="height: 29.7969px;">Opérations mathématiques.</td></tr><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">random</td><td class="align-left" style="height: 29.7969px;">Fonctions liées à l'aléatoire.</td></tr><tr style="height: 29.7969px;"><td class="align-center" style="height: 29.7969px;">tkinter</td><td style="height: 29.7969px;">GUI pour Python.</td></tr></tbody></table>

## Fonctions

Les fonctions permettent de créer un bloc d'instructions réutilisable à souhait.

Elle réalise généralement accomplir une tâche spécifique.

Les fonctions peuvent prendre ce qu'on appelle des paramètres, qui sont des variables fournis à la fonction, qu'elle pourra utiliser :

```python
def addition(a, b):
    resultat = a + b
    return resultat

"""
Ici, la fonction addition() a comme paramètres 'a' et 'b'.
Elle stocke dans une variable locale le résultat de l'addition des deux valeurs contenues dans 'a' et 'b'.
Puis elle renvoie le résultat pour qu'on puisse l'utiliser en dehors de la fonction.
"""
```

On peut donc appeler cette fonction et afficher le résultat de l'opération :

```python
a=4
b=5

resultat=addition(a, b)
print(resultat)
```

#### Main

```python
def main():
    print("Hello world!")

if __name__ == "__main__":
    main()

```

## Fichiers

En Python, on peut nativement ouvrir des fichiers pour traiter leur contenu :

```python
fichier = open("mon_fichier.txt", "r")
contenu = fichier.read()
fichier.close()
```

La fonction open() prend en premier paramètre le chemin du fichier à ouvrir, et en deuxième paramètre il prend le mode d'ouverture. Voici un bref résumé des modes d'ouverture :

<table border="1" id="bkmrk-modes-d%27ouverture-de" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 50%;"></col><col style="width: 50%;"></col></colgroup><tbody><tr><td class="align-center">Modes d'ouverture</td><td class="align-center">Descriptions</td></tr><tr><td class="align-center">**r**</td><td>Ouvre un fichier en lecture seule.</td></tr><tr><td class="align-center">**w**</td><td>Ouvre un fichier en écriture.</td></tr><tr><td class="align-center">**a**</td><td>Ouvre un fichier en ajout.</td></tr></tbody></table>

## Exceptions

Les exceptions permettent de prendre en compte qu'il y a une possibilité d'erreur lors du traitement d'une bloc d'instructions et d'agir en conséquent.

```python
try:
    resultat = 10 / 0
except ZeroDivisionError:
    print("Division par zéro impossible")
```

<span style="text-decoration: underline;">Remarque :</span> Ici, le cas de l'erreur **ZeroDivisionError** est traité mais le mot clé **except** sans précision du type d'erreur capture toutes les erreurs éventuelles.

## POO

La programmation orientée objet (POO) est une physionomie de programmation où l'on travaille non pas avec des variables simples, mais avec des **objets** qui ont des **attributs** (caractéristiques) et des **méthodes** (savoir-faire).

Un objet est définis par une **classe** que l'on pourrait représenter comme étant le schéma descriptif de l'objet.

#### Classes

Voici un exemple de classe :

```python
class MaClasse:
    def __init__(self, nom):
        self.nom = nom

    def afficher_nom(self):
        print("Mon nom est", self.nom)
```

On dit que la classe **MaClasse** a comme attribut **self.nom** et comme méthode **self.\_\_init\_\_()** ainsi que **self.afficher\_nom()** .

La méthode **\_\_init\_\_()** est présente dans toutes les méthodes et est exécutée lors de l'**instanciation** (la création) de l'objet.

Elle doit définir les attributs de la classe et prend souvent en paramètre un ou plusieurs attributs de la classe (ici, c'est le cas avec le **nom**) .

Le mot-clé **self** désigne l'objet lui-même.

#### Instanciation d'objet

Maintenant que la classe est définie, nous pouvons instancier (créer) un ou plusieurs objets à partir de la classe :

```python
objet = MaClasse("Objet")
```

#### Méthodes

Les méthodes sont des fonctions propres à la classe :

```python
objet.afficher_nom()
```

La particularité des méthodes par rapport aux fonctions est la présence de l'objet (self) qui est implicitement passé en premier paramètre à la méthode lors de l'appel de celle-ci.

<span style="text-decoration: underline;">Remarque :</span> Puisque self ne doit pas être passé, il faut considérer le premier paramètre de l'appel de la méthode comme étant le deuxième décrit dans la classe.

#### Getter / Setter

Les décorateurs permettent de définir des **getters** et des **setters** pour vos attributs.

Pour rappel, un getter est une méthode qui retourne la valeur d'un attribut et un setter permet de définir la valeur d'un attribut depuis l'extérieur de la classe.

L'implémentation de ces deux types de fonction doit se faire avec des **décorateurs @** en python.

D'ailleurs, il est obligatoire de définir un getter et un setter même si vous n'en souhaitez qu'un des deux.

 Voici comment implémenter les guetters et setters :

```python
class MaClasse:
    def __init__(self):
        self._ma_valeur = None  # Attribut privé, convention avec un préfixe _

    @property
    def ma_valeur(self):
        return self._ma_valeur

    @ma_valeur.setter
    def ma_valeur(self, nouvelle_valeur):
        if nouvelle_valeur < 0:
            raise ValueError("La valeur ne peut pas être négative")
        self._ma_valeur = nouvelle_valeur

# Utilisation de la classe
objet = MaClasse()

# Accès à l'attribut via le getter
print(objet.ma_valeur)  # Affiche None car aucune valeur n'a été définie encore

# Utilisation du setter pour définir une nouvelle valeur
objet.ma_valeur = 42

# Accès à la nouvelle valeur via le getter
print(objet.ma_valeur)  # Affiche 42

# Essayons de définir une valeur négative, cela lèvera une exception
objet.ma_valeur = -5  # Cela lève une ValueError

```

#### Héritage

Une classe en python peut donner naissance à une ou plusieurs classes dérivées qui bénéficieront des attributs et des méthodes de la classe mère ainsi que de leur nouveaux potentiels attributs et méthodes.

Voici un exemple avec un classe mère Animal et deux classes filles Chien et Chat :

```python
# Classe mère (superclasse)
class Animal:
    def __init__(self, nom, age):
        self.nom = nom
        self.age = age

    def courir(self):
        print("Je cours")


# Classe enfant (dérivée)
class Chien(Animal):
    def __init__(self, nom, age, race):
        # Appeler le constructeur de la classe parente
        super().__init__(nom, age)
        self.race = race

    def parler(self):
        return "Woof!"

# Classe enfant (sous-classe) différente
class Chat(Animal):
    def __init__(self, nom, age, couleur):
        # Appeler le constructeur de la classe parente
        super().__init__(nom, age)
        self.couleur = couleur

    def chasser(self):
        return "RIP la souris..."

# Créer des instances d'objets
mon_chien = Chien("Rex", 3, "Labrador")
mon_chat = Chat("Misty", 2, "Gris")

# Utiliser les méthodes héritées
print(mon_chien.nom)  # Affiche "Rex"
print(mon_chat.age)   # Affiche 2

# Appeler les méthodes spécifiques à chaque classe enfant
print(mon_chien.parler())  # Affiche "Woof!"
print(mon_chat.chasser())   # Affiche "RIP la souris..."

```

#### Polymorphisme

Le polymorphisme permet de redéfinir les méthodes de la classe mère dans les classes filles afin de modifier le comportement de cette méthode lors de son exécution.

Voici un exemple concret :

```python
# Classe parente (superclasse)
class Animal:
    def __init__(self, nom):
        self.nom = nom

    def parler(self):
        pass  # Cette méthode sera redéfinie dans les classes filles

# Classe fille
class Chien(Animal):
    def parler(self):
        return "Woof!"

# Classe fille différente
class Chat(Animal):
    def parler(self):
        return "Miaou!"

# Fonction qui utilise le polymorphisme
def faire_parler(animal):
    print(f"{animal.nom} dit: {animal.parler()}")

# Créer des instances d'objets
mon_chien = Chien("Rex")
mon_chat = Chat("Misty")

# Utiliser la fonction avec différentes instances
faire_parler(mon_chien)  # Affiche "Rex dit: Woof!"
faire_parler(mon_chat)   # Affiche "Misty dit: Miaou!"

```

## Arguments

Lors de l'exécution de vos scripts, il est possible de passer au script des arguments pour s'en servir comme variables :

```bash
python monScript.py <ARG_1> <ARG_2> <ARG_3>
```

À l'aide du module standard **sys**, il est possible de récupérer ces arguments directement depuis une liste :

```python
import sys

for arg in sys.argv[]:
  print(arg)
```

Si nous faisons cela, nous verrons apparaître tous les arguments ainsi que le nom du fichier du script dans **sys.argv\[0\]** puisqu'il est considéré comme le premier paramètre.

Pour corriger cela et ne récupérer que les vrais arguments, nous pouvons faire comme cela :

```python
for arg in arguments[1:]:
  print(arg)
```

# [Python] Environnements virtuels

## Introduction

Les environnements virtuels en python ont pour but de créer une sorte de conteneur avec les bibliothèques afin de ne pas les installer sur son poste et ainsi travailler proprement.

## Prérequis

- **Python**
- **Pip**

## [![image.png](https://wiki.neopipe.fr/uploads/images/gallery/2023-09/scaled-1680-/kQJimage.png)](https://wiki.neopipe.fr/uploads/images/gallery/2023-09/kQJimage.png)

## Installation package venv

```
sudo apt install python3-venv
```

## Configuration d'un environnement virtuel

Tout d'abord, lancez l'initalisation de l'environnement virtuel (ici, env est le **nom** de l'environnement virtuel) :

```
python3 -m venv env
```

Activez l'environnement virtuel :

```
source env/bin/activate
```

Vous pouvez désormais installer vos modules et exécuter votre code normalement.

## Création d'un environnement virtuel global

L'objectif est de créer un environnement virtuel global qui sera **persistent** sur le système.

Pour cela, créez l'environnement virtuel:

```bash
python3 -m venv ~/.local/share/global-env
```

Ajoutez cette commande à la fin de votre fichier **.bashrc** ou **.zshrc** :

```bash
source ~/.local/share/global-env/bin/activate
```

De cette manière, l'environnement virtuel sera chargé constamment.

## Suppression d'un environnement virtuel

Pensez à désactiver l'environnement :

```
deactivate
```

Et à supprimer le répertoire concerné :

```
rm -rf env/
```

# [Python] Serveur web

## Introduction

Cette page décrit comment lancer un serveur web rapidement en Python.

Cela peut être utile pour débugger ou partager des fichiers.

Cependant, celui-ci ne supportera pas php.

## Déployer un serveur web

La racine de votre serveur web sera votre répertoire courant :

```bash
python3 -m http.server
```

<span style="text-decoration: underline;">Remarque :</span> Par défaut, le port **8000** sera utilisé.

Pour mettre le port **80**, il vous faudra les droits root et lancer la commande suivante :

```bash
python3 -m http.server 80
```

# [Python] Threads

## Introduction

En informatique un thread est une tâche en charge d'une partie d'un programme.

Plusieurs threads peuvent être exécutés en parallèle et c'est là que vient l'intérêt de leur utilisation.

Un thread execute en général une fonction et une fois que celle-ci est terminée, le thread s'éteint.

## Manuel

- Tout d'abord, importer la bibliothèque adéquate à l'utilisation des threads :

```python
import threading
```

- Ensuite, déclarez vos threads :

```python
myFirstThread = threading.Thread(target=myFirstFunction)
mySecondThread = threading.Thread(target=mySecondFunction)
```

- Démarrez vos threads :

```python
myFirstThread.start()
mySecondThread.start()
```

- Si vous souhaitez attendre la fin de l'execution de votre thread avant de passer à la suite du code :

```python
myFirstThread.join()
mySecondThread.join()
```

# [Python] Socket

## Introduction

En python, pour créer des applications en réseau il faut passer par des **sockets** qui est un objet de communication réseau qui permet d'échanger de l'information sur la couche IP.

[![image.png](https://wiki.neopipe.fr/uploads/images/gallery/2023-12/scaled-1680-/4DHimage.png)](https://wiki.neopipe.fr/uploads/images/gallery/2023-12/4DHimage.png)

## Codage

#### Quelques explications

Le socket est composé :

- D'un **protocole** de communication (<span style="text-decoration: underline;">SOCK\_STREAM</span> pour TCP et <span style="text-decoration: underline;">SOCK\_DGRAM</span> pour UDP).
- D'une **adresse IP** source et destination.
- D'un **port** source et destination.
- D'un **message** à transporter.

#### Code serveur

```python
import socket

SERVER_IP = '127.0.0.1'
SERVER_PORT = 1234

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_socket.bind((SERVER_IP, SERVER_PORT))

server_socket.listen(5)
print("Serveur en attente de connexions...")

client_socket, client_address = server_socket.accept()
print(f"Connexion entrante de {client_address}")

try:
    data = client_socket.recv(1024)
    print("Message du client :", data.decode())

    response = "Message reçu par le serveur!"
    client_socket.sendall(response.encode())

except ConnectionResetError:
    print("La connexion a été réinitialisée par le client.")

finally:
    client_socket.close()

server_socket.close()
```

#### Code client

```python
import socket

SERVER_IP = '127.0.0.1'
SERVER_PORT = 1234

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    client_socket.connect((SERVER_IP, SERVER_PORT))
    print("Connecté au serveur.")

    message = "Bonjour, serveur!"
    client_socket.sendall(message.encode())

    data = client_socket.recv(1024)
    print("Message du serveur :", data.decode())

except ConnectionRefusedError:
    print("La connexion a été refusée. Assurez-vous que le serveur est en cours d'exécution.")

finally:
    client_socket.close()

```

# [Python] BDD SQLite3

## Introduction

Python prend en charge la manipulation de base de donnée notamment avec SQLite3.

[![image.png](https://wiki.neopipe.fr/uploads/images/gallery/2024-02/scaled-1680-/JaIimage.png)](https://wiki.neopipe.fr/uploads/images/gallery/2024-02/JaIimage.png)

## Manuel

#### Connexion à la base de donnée

```python
import sqlite3
conn = sqlite3.connect('ma_base_de_donnees.db')
cur = conn.cursor()
```

#### Création d'une table

```python
cur.execute('''CREATE TABLE IF NOT EXISTS ma_table (
                id INTEGER PRIMARY KEY,
                nom TEXT NOT NULL,
                age INTEGER)''')
conn.commit()
```

#### Insertion de données

```python
cur.execute("INSERT INTO ma_table (nom, age) VALUES (?, ?)", ('Jean', 30))
conn.commit()
```

#### Sélection de données

```python
cur.execute("SELECT * FROM ma_table")

rows = cur.fetchall()
for row in rows:
    print(row)
```

#### Mise à jour des données

```python
cur.execute("UPDATE ma_table SET age = ? WHERE nom = ?", (40, 'Jean'))
conn.commit()
```

#### Suppressions des données

```python
cur.execute("DELETE FROM ma_table WHERE nom = ?", ('Jean',))
conn.commit()
```

#### Fermeture de la connexion à la base de donnée

```python
# Fermeture du curseur et de la connexion
cur.close()
conn.close()
```