Python* (mes notes)

Auteur:

J.Soranzo

Entity:

VoRoBoTics [1]

Date:

Août 2020

mise à jour:

04/02/2023

Disclaimer

Ce fichier est constitué de mes notes personnelles sur Python. Il ne s’agit en aucune manière d’un cours ou quoi que ce soit d’autre dans le genre. Ce serait plutôt un pense-bête.

Pourquoi les gens aiment Python

Parce qu’il est comme ARDUINO, il permet d’arriver simplement et rapidement à un résultat concret.

On peut tout tester dans la console ! !c’est le couteau suisse du programmeur.

Supprime la nécessité d’apprendre un IDE

Python c’est 31 mots clé !

Docs

La librairie standard [2]

Index des package sur Pypi Python Package Index [3]

Avertissement

Toute la doc Python est téléchargeable sur https://www.python.org/ rubrique documentation dans plusieurs langues !

Note personnelle : ne pas oublier le fichier de notes Freeplane (à convertir en notes Sphinx…)

S’y trouvent :

  • gestionnaires graphiques
    • pygame

    • pyQt

  • Astuces d’autres

  • perfectionnement opencs
    • argparse

    • logging

    • pdb

    • mathplotlib…

  • A revoir…

  • Les autres chapitre opencs
    • regex

    • time

    • prog system

    • multithreading

    • unitest

Outils, IDE…

Miniconda, conda

Utilisation de miniconda3

Conda documentation [4]

Conda cheat sheet [5]

Mes commandes vraiment utiles:

conda create -n toto (par défaut python 2.7)
conda create -n toto python=3.6
conda list -n toto
conda remove --name toto --all
conda info --envs
conda --version

Conda n’est pas lié à un répertoire !

conda env create -p ../venv -f locks/conda.yml
Commande fonctionnelle liste des packages communs
conda  create versus conda env create : la version avec env permet de créer ou d'exporter
un environnement grâce aux fichiers yml

Pour exporter un environnement:

conda env export --name toto --file constructor_of_toto.yml

Conda est lié à un dépôt. qui peut contenir des package différents d’une plateforme à l’autre. Comme sur Raspberry pi: conda info indique ce dépôt:

channel URLs :  https://repo.continuum.io/pkgs/free/linux-armv7l/
                https://repo.continuum.io/pkgs/free/noarch/
                https://repo.continuum.io/pkgs/pro/linux-armv7l/
                https://repo.continuum.io/pkgs/pro/noarch/

Donc sur RPi avec conda, il ne faut même pas espérer faire du python autre que 3.4 et 2.7

Et bien si, grâce à conda config --add channels rpi puis conda search --full-name python

Source : site : ANegron’s Blog How to install Conda and Docker on your Raspberry Pi [6]

Jupiter

A revoir

Project Jupyter [7] exists to develop open-source software, open-standards, and services for interactive computing across dozens of programming languages.

Prise en main de l’outil Jupyter [8]


A revoir

Scrapy [9] : permet de « grater » des page web

Python Code Quality: Tools & Best Practices [10]

Documenter du code Python et relation avec Sphinx

Je ne trace ici que les écueils auxquels j’ai été confronté. Il y a de nombreux sites qui traitent du sujet.

Un très bon site pas à pas : blog.flozz.fr [11]

Cela passe par docstring """ """

Sphinx permet de transformer des fichier restructured text en fichier latex, html… Mais, il n’extrait pas tout seul les docstring du code Python pour cela il lui faut une extension.

Comme autodoc qu’il faut ajouter dans le fichier config.py:

extensions = [
    'sphinx.ext.autodoc'
]

Il faut créer une arborescence documentaire de fichiers rst et y placer des directives comme:

.. automodule:: CRdcGui

.. autoclass:: CRdcGUI
    :members:
    :undoc-members:

Il peut être aussi fort util de renseigner le chemin vers les sources au début du fichier config.py:

import os
import sys
sys.path.insert(0, os.path.abspath('../sources'))

Éléments de langages

Annotations

Depuis Python 3.6, on peut annoter les paramètres d’une fonction ou les

voir:

https://zestedesavoir.com/tutoriels/954/notions-de-python-avancees/2-functions/2-annotations-signatures/

Notations / opérateurs

  • Notation hex : C’est 0x33

  • Binaire : 0b

  • Octal : 0o

Long integer 123L

Code sur plusieurs lignes c’est avec le caractères \

Vrai, faux

True et False avec une majuscule

Opérateurs

Arithmétique : je ne parle pas des courants:

%   Modulo
**  Puissance
//  Division entière

Tous les opérateurs arithmétiques peuvent être combinés avec = comme //= ou %= …

les opérateurs logiques:

X|Y X ou Y, ^ ou exclusif, & pour et, ~ pour l'inversion

Les opérateurs booléens:

X or Y, X and Y et not X

Les opérateurs de comparaisons:

== et != ou <>

Tuples

Ce sont simplement des listes non modifiables syntaxe : () à l d [] par rapport à la syntaxe d’une liste.

Listes

Toutes les méthodes de list:

>>> listMethode[listMethode.index('clear'):listMethode.index('sort')]
'clear', 'copy', 'append', 'insert', 'extend', 'pop', 'remove', 'index', 'count', 'reverse'

Création:

toto = [1, 5, "tutu", 16.9, (12,3), ["Pierre", "05.12.34.56.78"]]
toto = list([14,5,12])
truc = list() #pour une liste vide
ou encore troc = []

Éléments de syntaxe: les crochets.

L’intérêt:

>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons)) #liste de tuples (index, éléments)
    permet de créer une nouvelle liste avec des commandes !

Ajout:

maList.append(nvlElement)
attention pas de valeur de retour. Travaille directement sur maList
maList.insert(6, "titi")
maList.extend(autreListe)

Concaténer 2 listes:

6 méthodes

for i in test_list2 :
    test_list1.append(i)

list3 = list1 + list2

res_list = [y for x in [list1, list2] for y in x]

extended_list.extend(listtoadd)

res_list = [*test_list1, *test_list2]

res_list = list(itertools.chain(test_list1, test_list2))
Accès

Pour accéder à un élément : malist[indice] indice commence évidement à 0 Pour accéder à plusieurs élément consécutifs : malist[x:y]

Suppression:

maList.remove("tutu")
malist.pop() ou maList.popleft()
ou del maList[3]

Longueur La méthode count permet de conter le nombre d’occurence d’un élément dans la liste. Il faut utiliser la fonction len(malist)

pile et queue Elle peuvent être utilisées en pile ou en queue cf. <https://docs.python.org/3.1/tutorial/datastructures.html>

Grace à pop pour les piles et popleft pour les files d’attente.

insert(0 , item) et pop() : pour les queues.

append() et pop() : pour les piles ou insert(0, item) et pop(0) semble moins efficace (faut tout décaler)

Concept très intéressant de tableau qui se vide au fur et à mesure de son traitement. Quand le tableau est vide, on a fini (récursivité…). De plus pop renvoi l’élément retirer ;-)

test d’appartenance:

if variable in maListe:
    instruction in !

Avertissement

attention à la copie de liste list2=list1 ne copie que le nom (l’adresse) pas les données.

Mais on peut utiliser les compréhensions de listes:

list2 = [x for x in list]
ou plus simplement list2 = list(list1)
ou encore list2=list1.copy()

Compréhension de listes ou listes en intension

La syntaxe est : [ action iterable]:

[ 'a' for i in rang(10) ]
    noter que i n'est pas nécessairement utilisé dans action

C’est assurément un des grandes forces de Python et un élément de programmation nouveau. L’idée est de créer un liste en une seule ligne Voir les comprehensions de liste sur Sam et Max [12]

Ca fabrique une liste !

[expression for element in sequence]
moyen de filtrer les listes
    mais pas que
    [expression for element in sequence if condition]
List comprehensions provide a concise way to create lists from sequences. Common applications
are to make lists where each element is the result of some operations applied to each member
of the sequence, or to create a subsequence of those elements that satisfy a certain condition.

exemples

[nb * nb for nb in liste_origine]
    c'est en ça que python devient for (on parcours la liste en une seul ligne. L'astuce est de créer une nouvelle liste
    [nb for nb in liste_origine if nb % 2 == 0]
        encore plus fort
    [str(round(355/113, i)) for i in range(1, 6)]
        donne : [’3.1’, ’3.14’, ’3.142’, ’3.1416’, ’3.14159’]
    ou encore:
        [x*y for x in vec1 for y in vec2]

Création d’une liste de n éléments identique:

>>> malist =[]
>>> for i in range(10):
    malist.append(2)

mais:

truc=[truc.append(5) for i in range(10)] ne marche pas
mais truc = [ 5 for i in range(10) ] marche

Remarque : le for element in sequence est le même que dans la syntaxe d’une boucle for. On peut considérer la compréhension de liste comme une boucle for condensée.

Astuce

  • lire les compréhension de liste de la droite vers la gauche.

  • maliste.append([1,2,5]) n’ajoute qu’un seul élément à la liste qui est [1,2,5]

  • en revanche maliste += [2,3,5] fonctionne et ajoute 3 élément à la liste ou .extend()

  • la longueur de la liste malist.len() n’existe pas il faut faire len(list)

  • maliste.append(2,3,5) ne fonctionne pas

Liste et paramètres de fonction la syntaxe au niveau definition est

def fonction(*parametres):

la fonction reçoit un tuple des paramètres.

L’appel d’une telle fonction peut se faire fonction( 1, 3, 6) ou fonction(*malisteDeParametres)

Cela est réservé au paramètres non nommés et on peut combiner des paramètre et une liste. La liste doit se trouver en dernier ainsi que des paramètres nommés qui se trouveront après.

enumerate Voir Enumerate

Exemples en vrac:

list(range(10)) #! crée une lise de 0 à 9
[x*y for x in vec1 for y in vec2]
avec des un if :
listeRequetes = [ req[5][1:] for req in tablesEchanges if req[2] == 'VOL Numéro 1']
# tablesEchanges est une liste de listes

Dictionnaires

Doc officielle sur les dictionnaires [13]

Mot clé : dict, création: maVar = dict()

Éléments de syntaxe: les accolades et les :

On peut aussi créer des dictionnaires déjà remplis

placard = {"chemise":3, "pantalon":6, "tee-shirt":7} - on notera les accolades

Remplissage : maVar[ clé ] = valeur

Clé et valeur peuvent être de tout type (y compris des tuples par exemple et y compris dans le même dictionnaire).

Exemple:

dico['a',0]="toto" on note que les parenthèses du tuple sont facultatives
>>> mon_dictionnaire["pseudo"] = "Prolixe"
>>> mon_dictionnaire["mot de passe"] = "*"
>>> mon_dictionnaire
{'mot de passe': '*', 'pseudo': 'Prolixe'}

    la clé est par conception unique
        maVar[ "ici" ] = 234
        ...
        puis maVar[ "ici" ] = 'RESTE'
            Reste écrase 234.

{ “banane”, “pomme”, “citron” } n’est pas un dictionnaire sans valeurs. C’est un set ou ensemble. A la différence des liste, il ne peu contenir 2 fois la même valeur.

Les dictionnaires peuvent servir de paramètre nommés d’une fonction comme les listes pour les paramètres non nommés.

[ a for a in dico.keys()] donne la liste des clés

[ a for a in dico.items()] donne une liste de tupple et pas un dictionnaire:

{'NADIA': 0, 'JOJO': 14}
[('NADIA', 0), ('JOJO', 14)]

Enumerate

C’est un mot clé et une fonction qui retourne un tuple(indice, valeur) et qui s’applique à tous les iterators.

Différence:

lsie = [12,35,'rien',65.3]
>>> for elt in lsie:
    print(elt)

12
35
rien
65.3
>>> for elt in enumerate(lsie):
    print(elt)

(0, 12)
(1, 35)
(2, 'rien')
(3, 65.3)
>>>

Fonctions

Syntaxe:

def fonctionName(parametres, param = defValue) :
    return a, b, c,d

Les fonction peuvent retourner plusieurs valeurs.

Pas de surcharge

fonction lambda ? f = lambda x: x * x

Intérêt ? Écrire du code plus concis.

lambda est un mot clé

les fonctions lambda sur developpez [14]

sur open classroom meilleur explication de la fonction lambda sur Openclassroom [15]

Exemple du tri avec une lambda sur Openclassroom [16]

En résumé: on met dans une variable une fonction pour pouvoir l’appeler ensuite sauf qu’on ne donne pas de nom à la fonction.

Fonctions avec nombre paramètre indéterminé:

def fonction_inconnue(*parametres):
    *parametre défini un tuple (rien à voir avec les pointeurs ?!
    on peut mixer
        def fonction_inconnue(nom, prenom, *commentaires):
>>> def fonction_inconnue(*parametres):
...     """Test d'une fonction pouvant être appelée avec un nombre variable de paramètres"""
...
...     print("J'ai reçu : {}.".format(parametres))
...
>>> fonction_inconnue() # On appelle la fonction sans paramètre
J'ai reçu : ().
>>> fonction_inconnue(33)
J'ai reçu : (33,).
>>> fonction_inconnue('a', 'e', 'f')
J'ai reçu : ('a', 'e', 'f').
>>> var = 3.5
>>> fonction_inconnue(var, [4], "...")
J'ai reçu : (3.5, [4], '...').
>>>

Une liste peu devenir paramètres d’une fonction, vachement puissant:

>>> liste_des_parametres = [1, 4, 9, 16, 25, 36]
>>> print(*liste_des_parametres)

Les décorateurs

Pour schématiser, une fonction modifiée par un décorateur ne s’exécutera pas elle-même mais appellera le décorateur. C’est au décorateur de décider s’il veut exécuter la fonction et dans quelles conditions. (from openclassroom). C’est un moyen simple de modifier le comportement d’une fonction. Un décorateur est une fonction (qu’il faut donc définir de la même manière qu’une autre fonction) qui est appelé avant l’appel de la fonction elle-même. Il se place juste une ligne avant la définition de la fonction et est précédé par @.

On peut créer des décorateurs qui accepte des paramètres et dans ce cas on atteint vite 3 niveaux de définition de fonctions imbriquées. Cf. OpenClassromm

Autres explication très détaillée par Simeon Franklin [17] en anglais.

partial() appartient functool

super() sujet : class, hiérarchie

Permet d’appeler explicitement une méthode de la classe mère si celle-ci est redéfinie dans la classe fille. Par exemple init


Modules

C’est tout simplement un fichier .py qui contient des variables, des fonctions ou des classes.

Plusieurs mots clés sont associés à la notion de module:

from
import
as

Plusieurs syntaxes sont possible:

import maths
from maths import sqr
import maths as mathematiques
from myModule import *
    importe  myModule dans l'espace de nom principal
    Si myModule est un package alors les noms des modules qu'il contient sont créés dans
    l'espace des noms courants ainsi que les noms de ses sous-packages mais pas de leurs modules
    respectifs.
import crée un espace de nom (*from OpenClassroom*)

Astuce:

diff entre import os et from os import *
dans le premier on est obligé de mettre os.fonction()
dans le second cas les fonctions font parties de l'espace de noms courant.
Mais quand il s'agit d'un package avec des sous package
    from PyQt5.QtWidgets import QApplication,QWidget

Note

  • Lister les modules accessibles : help('modules')

  • Lister les package installés : pip list ou pip freeze

Faire un test de module dans le module-même:

if __name__ == "__main__":
        code à executer

Le code qui suit cette ligne n’est exécuté que si la condition est vrai. En d’autres termes si le module est programme principal et non issu d’un import.

On peut intégrer l’aide dans le module ou dans la fonction:

"""visiblement en plaçant le texte en début de bloc (par exemple just entre le nom de la
fonction et le reste du code et en encadrant le texte avec un tripe double cote"""
Ou carrément en début de module

help("nomPackage.nomFonction ou nomPackage")

On peut même intégrer un test automatique cf. doctest. The doctestmodule makes unit testing as simple and painless as possible. To use it all we need to do is add examples to our docstrings, showing what we would type into the interactive Python interpreter (or IDLE) and what response we expect back.

A revoir 24/08/2020


Les exceptions

On peut intercepter les erreurs (ou exceptions) levées par notre code grâce aux blocs try except. La syntaxe d’une assertion est assert test:. Les assertions lèvent une exception AssertionError si le test échoue.

On peut lever une exception grâce au mot-clé raise suivi du type de l’exception.

Mots clés : try et except (dans sa version la plus basic)

Il est plus que vivement conseillé de préciser un type d’erreur derrière except au risque de capturer toutes les exceptions y compris ctrl+c par exemple !

Un grand classique d’utilisation est la saisie de valeur:

>>> while True:
...     try:
...         x = int(input("Please enter a number: "))
...         break
...     except ValueError:
...         print("Oops!  That was no valid number.  Try again...")

Il est également possible de faire suivre l ’instruction try de plusieurs blocs except. Chacun d’entre eux traitant un type d’erreur spécifique:

except
    Except error_name1:
    Except error_name2:
else
finaly
    A finally clause is always executed before leaving the try statement, même s'il y a un
    return dans le bloc
pass
assert
    Si le test renvoie True, l'exécution se poursuit normalement. Sinon, une exception
    AssertionError est levée.
    Il faut voir cela comme une affirmation (une assertion) dans si elle n'est pas correcte
    alors erreur.

Exemples:

try:
    resultat = numerateur / denominateur
except NameError:
    print("La variable numerateur ou denominateur n'a pas été définie.")
except TypeError:
    print("La variable numerateur ou denominateur possède un type incompatible avec la division.")
except ZeroDivisionError:
    print("La variable denominateur est égale à 0.")
else:
    print("Le résultat obtenu est", resultat)
finally:
    # Instruction(s) exécutée(s) qu'il y ait eu des erreurs ou non
except type_de_l_exception: # Rien ne doit se passer en cas d'erreur
    pass
        annee = input("Saisissez une année supérieure à 0 :")

try:
    annee = int(annee) # Conversion de l'année
    assert annee > 0
except ValueError:
    print("Vous n'avez pas saisi un nombre.")
except AssertionError:
    print("L'année saisie est inférieure ou égale à 0.")

Sortir d’une boucle infinie par une iterruption clavier

Les packages

Il s’agit tout simplement d’un répertoire de module

On peut importer un pakage entier ou seulement un module dans le package ou seulement une fonction d’un module dans un package.

from package.fonctions import table
import tkinter as tk
from tkinter import messagebox
from tkinter import ttk

On trouve de nombreux package et fonctions dans C:Python34Lib

Un package doit obligatoirement contenir un fichier _init_.py même vide. Ceci n’est plus vrai depuis la version 3.3

Liste des package hyper courant:

  • random : fonctions permettant de travailler avec des valeurs aléatoires

  • math : toutes les fonctions utiles pour les opérations mathématiques (cosinus,sinus,exp,etc.)

  • sys : fonctions systèmes

  • os : fonctions permettant d’interagir avec le système d’exploitation

  • time : fonctions permettant de travailler avec le temps

  • calendar : fonctions de calendrier

  • profile : fonctions permettant d’analyser l’execution des fonctions

  • urllib2 : fonctions permettant de récupérer des informations sur internet

  • re : fonctions permettant de travailler sur des expressions régulières

Les fichiers

outres le classique fichier = open('gilename', 'atttrib') avec comme attribut:

r, w, X, a, b, t, +

X création exclusive, échoue si le fichier exsite déjà.

  • : ouvre en modification (lecture et écriture)

Il y a aussi la syntaxe:

with open('file', 'wb') as fichier:

Avantage : pas besoin de close

Un mot sur le module pickel: il permet la sérialisation de variable (cf doc officielle chapitre 12). Il utilise 2 méthodes : dump et load. C’est très utile pour stocker des variables et les recharger par la suite.

Décrit dans openclassroom pickle [18]

Dans tous les exemples que j’ai pu trouvé, on n’y voit jamais qu’une seule variable aussi complexe soit elle. J’ai lu un post qui disait de regrouper ces variables dans une liste avant de les sauvegarder

Exemple simpliste:

import os

file_path = "D:/data123.txt"

#check if file is present
if os.path.isfile(file_path):
    #open text file in read mode
    text_file = open(file_path, "r")

    #read whole file to a string
    data = text_file.read()

    #close file
    text_file.close()

    print(data)

Autre exemple encore plus simpliste:

with open('file.txt') as f:
    contents = f.read()
    print(contents)

Les objets

classe template:

class nomClasse: # Définition de notre classe
"""Classe documentation"""

    def __init__(self): # Notre méthode constructeur
        """Documentation du constructeur"""
        self.attr1 = valeurInitiale

    def methode(self, param1):
        """doc"""
        #code

Importance du paramètre self! Il faut mettre son grain de self un peu partout

créer une instance:

Attention : var = nomclasse ne crée pas d'instance !!!
var = nomClasse() oui

constructeur:

def __init__(self, var1, var2...)
    # double underscore init double underscore
    self.attribut1 = var1...

le constructeur est considéré comme une méthode spéciale au même titre que __dict__
est un attribut spécial

Méthodes et self:

on peut appeler une méthode depuis l'objet instancié ou depuis sa classe
    a = objet()
a.methode(autreVar)
ou objet.methode(a, autreVar)

Ceci provient du fait que les méthodes ne sont pas recopiées dans chaque objet instancié seulement les attributs sont différents

Méthodes commence toutes avec self comme premier paramètre. Sauf les méthodes statiques et les méthodes de classe

Getters et setters: bien que la notion de private n’existe pas, on peut, grace au mot clé property créer des accesseurs et mutateurs

Exemple:

class Personne:
 """Classe définissant une personne caractérisée par :
 - son nom ;
 - son prénom ;
 - son âge ;
 - son lieu de résidence"""


def __init__(self, nom, prenom):
    """Constructeur de notre classe"""
    self.nom = nom
    self.prenom = prenom
    self.age = 33
    self._lieu_residence = "Paris" # Notez le souligné _ devant le nom


def _get_lieu_residence(self):
"""Méthode qui sera appelée quand on souhaitera accéder en lecture
    à l'attribut 'lieu_residence'"""

    print("On accède à l'attribut lieu_residence !")
    return self._lieu_residence


 def _set_lieu_residence(self, nouvelle_residence):
    """Méthode appelée quand on souhaite modifier le lieu de résidence"""
    print("Attention, il semble que {} déménage à {}.".format( \
            self.prenom, nouvelle_residence))
    self._lieu_residence = nouvelle_residence


# On va dire à Python que notre attribut lieu_residence pointe vers une
# propriété
lieu_residence = property(_get_lieu_residence, _set_lieu_residence)

Autre façon de déclarer les getters et setteurs:

def _width(self):
    return self.__width
def _setWidth(self, width):
    # Perform some computation
    self.__width = width
width = property(fget=_width, fset=_setWidth)
#on notera le jeu des doubles __ dans self.__width et sa disparition dans width = property

Property permet de redéfinir un attribut en lui allouant des acesseur et mutateur. Cela permet de redéfinir le comportement des attributs sans casser le code utilisateur.

width est redéfini alors qu’à l’extérieur on fait tjrs objet.width

Autre façon de transformer une méthode en propriété: grâce au décorateur @property:

class Position:
def __init__(self, longitude_deg, latitude_deg):
    self.longitude_deg = longitude_deg
    self.latitude_deg = latitude_deg

@property
def longitude(self):
    return self.longitude_deg * math.pi / 180

Utilisation : position.longitude

Les méthodes spéciales: elles sont encadrées par __

Il en existe pour surcharger la plupart des opérateurs:

__add__ pour +
__gt__ pour >
__mul__ pour *
...
+=

La liste complète est énorme <https://www.mindmeister.com/fr/10510492/python-underscore>

Quelques unes parmi les plus intéressantes:

__init__
__del__
__repr__ pour l'affichage de l'objet
__str__ utilisée lors de la conversion de l'objet en chaîne ;-)
__getatr__
__setattr__
__delattr__
__iter__
__next__

Il y a aussi des « buildin functions » qui font le même boulot que ces méthodes:

getattr(objet, "nom") # Semblable à objet.nom
setattr(objet, "nom", val) # = objet.nom = val ou objet.__setattr__("nom", val)
delattr(objet, "nom") # = del objet.nom ou objet.__delattr__("nom")
hasattr(objet, "nom") # Renvoie True si l'attribut "nom" existe, False sinon

Celles des object conteneurs:

__getitem__
__setitem__
__delitem__
__contains__
__len__ équivalent de la fonction len(objet) <=> objet.__len__()

Permette de fournir des métadata également comme:

__autor__
__version__
__licence__

Certaines font vraiment partie du langage et d’autre tiennent plus de la convention de nommage. c’est le cas de version autor…

L’attribut spécial __dict__. Cet attribut est un dictionnaire qui contient en guise de clés les noms des attributs et, en tant que valeurs, les valeurs des attributs.

Héritage class fifille(maman).

Biltin function super():

Il est souvent nécessaire d'initialiser un objet
    __init__(self, param1, param2, ...)
    Pour une classe fille c'est pareil et en plus il faut faire appel à l'init de la class mere
    avec
        maman.__init__(self, param1, pram2,...) seulement ceux de la maman
        (les 2 liste de paramètres peuvent être différentes)

    ou avec
        super(fifille, self).__init__(param1, param2...)
        pas de self dans la liste des param de maman !
class C(B):
    def method(self, arg):
        super().method(arg)    # This does the same thing as:
        # super(C, self).method(arg)

Fonctions utiles : issubclass() et isinstance()

Héritage multiple: quand une classe hérite de plusieurs classes en parallèle:

classeFille(mereA, mereB)

L’héritage permet la surcharge des méthodes.

L’ordre de recherche d’un méthode correspond à l’ordre de déclaration:

  • fille

  • mere1

  • mere1parentes

  • mere2

  • mere2Parents

On peut à tout moment préciser la méthode appelée par nomClasse.nomMethode(self,…)

Simple underscore pour attributs et méthodes: Python does have a concept of « private »—objects with names that begin with a single leading underscore are considered to be private. As far as methods and instance variables are concerned, their privacy is merely a convention that we are invited to respect. And as for modules, private classes and functions, i.e., those whose name begins with a leading underscore, are not imported when using the from moduleName import syntax. Python also has a concept of « very private »—methods and attributes with names that begin with two leading underscores.

Very private objects are still accessible, but the Python interpreter angles their names to make it difficult to access them by mistake.

Il est possible aussi d’avoir des attributs de la class (static). Il faut les déclarer avant le constructeur.

On y accède avec le nom de la classe devant : nomClass.attrib1 +=1 pa exemple

Ainsi que des méthode de class avec le mot clé : cls + build in fonction classmethod()

Une méthode de classe a comme premier paramètre cls et pas self. Exemple:

class Compteur:
"""Cette classe possède un attribut de classe qui s'incrémente à chaque
fois que l'on crée un objet de ce type"""
objets_crees = 0 # Le compteur vaut 0 au départ

def __init__(self):
"""À chaque fois qu'on crée un objet, on incrémente le compteur"""
    Compteur.objets_crees += 1

def combien(cls):
"""Méthode de classe affichant combien d'objets ont été créés"""
    print("Jusqu'à présent, {} objets ont été créés.".format(cls.objets_crees))

combien = classmethod(combien)

Pour les méthodes static: ni self, ni cls + utiliser la fonction staticmethod

Métaclasse <https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python/233659-decouvrez-les-metaclasses>

L’idée est créer des classe dynamiquement c’est à dire pendant l’exécution. Fonctionnalité très avancées selon moi

Itérateur & générateur

Un itérateur est avant tout une classe qui va être chargé de parcourir l’objet conteneur : cf. opencs chapitre sur les boucles for [19] (ce lien ne mène plus à une info pertinante en 2024)

Voir aussi

SITE DE RESSOURCES

Qu’est-ce qu’un générateur en Python [20]

L’itérateur est créé dans la méthode spéciale __iter__ de la classe

Si on veut créer son propre itérateur pour sa propre classe, cela signifie qu’il faudra créer une nouvelle classe dont une instance est retournée pat __iter__.

Donc en général __iter__ fait un return monIterator(self)

L’itérateur a une méthode spéciale __next__. next() ou __next__ lève l’exceptions StopIteration en fin d’itération.

Il y a 2 fonctions spéciales python associées à ces méthodes : iter() et next().

Un générateur est une fonction (ou méthode) qui contient le mot clé spécial yield

Doc python sur les generator [21]

C’est un moyen plus simple de créer et de manipuler des itérateurs

L’avantage du générateur est qu’il n’est pas besoin de créer une class itérateur ni de méthode __next__ ni de lever l’exception de fin

Utilisation classique

iter( monGenerator() )
on peut créer des fonctions générateur independent de toute classe
    exemple : intervalle(5, 10) renvoi des nombre de 6 à 10

Les générateurs accepte des co-routines très puissant
    méthodes : .close() et .send()
        y a pas restart

Tout est sur openclassroom, chapitre sur les boucle for [22]

Il s’agit d’une fonction très avancée dans leur création.

If expression

Introduit avec la version 2.5 vise à faire la même chose que exp ?valeur si vrai:valeur si faux donc:

X if condition else Y
exemples:
result = 'even' if a % 2 == 0 else 'odd'
print (a if b else 0)

Astuces

tempo

import time

time.sleep(0.1) # en secondes
from time import sleep

sleep(0.1)

Sur les chaînes

Formater une chaîne:

"la chaine {1} à formater {0}".fomat( varZero, varUn)

Les chiffres dans les accolades sont facultatifs, il s’agit de la méthode format de la class intégrée str

Forme abrégée:

f"Ma variable vaut : {variabel}"

Tout est décrit en détail dans The Python Library Reference §Format String Syntax

Pour de l’hexa:

":2X"
print("Valeur hex = 0x{:04X}".format(a) )
print("Valeur hex = {:#04X}".format(a) ) # mais directement 0X devant le nombre
b=3.1425
print("Valeur flottant 3 décimale = {:.3f}".format(b) )

Autre forme:

# formatage d'une adresse
adresse = """
    {no_rue}, {nom_rue}
    {code_postal} {nom_ville} ({pays})"""
.format(no_rue=5, nom_rue="rue des Postes", code_postal=75003, nom_ville="Paris", pays="France")
print(adresse)

La class template à l’air bien aussi:

from string import Template
>>> s = Template(’$who likes $what’)
>>> s.substitute(who=’tim’, what=’kung pao’)
’tim likes kung pao’
for i in range(len(chaine))

Génère tous les indices d’une chaîne

Initialiser une chaîne avec n fois le même caractère: chain = "-"*10

Recherche d'une lettre dans un mot
    for lettre in mot_complet:
            if lettre in lettres_trouvees:
join str list
    a="toto" b=list(a) a=''.join(b)
Supprimer les espaces
    méthode strip, rstrip ou lstrip

pickling <https://docs.python.org/3/library/pickle.html>
    serialisation
    Chapitre 12 de la doc 3.4.4
    see also HDF5 et JSON

Chaîne en nombre et inversement
Chaîne en JSON

Retour à la ligne:

print("\n") #tout simplement !

Tester, tester, tester

Cela doit devenir un réflexe, on peut tout expérimenter dans la console Python des commandes seules mais aussi des bouts de codes qu’on a mis dans un fichier TOUT !

Jouer avec les fonctions, les classes dans des fichiers séparés, ça à l’air tout bête mais on peut mettre des fonctions, des classes dans des fichiers et jouer avec dans la console.

Importer ses fichiers avec from mon_fichier import *

Pour les tests réels du code on se tournera vers pytest [23] ou unitest [24]

if

C’est bête mais if: et elif:

et pas else if ou elsif

Où est python ?

c:\>where.exe python
C:\Users\nom\AppData\Local\Programs\Python\Python38\python.exe
avec Windows search : python
    En 2 fois
    ouvrir l'emplacement du fichier
        chemin du raccourci
    propriété du racourci
    ouvrir emplacement de la cible

Turtle

Turtle <https://docs.python.org/3.3/library/turtle.html?highlight=turtle>

Petit truc graphique rigolo, plus riche qu’on ne pourrait s’y attendre !

Toujours terminé les script avec la fonction done()

Une vidéo sympa <https://www.youtube.com/watch?v=pxKu2pQ7ILo>

sur l’Aide

help et help short form:

object.__dict__
dir(objet)

Les 2 ne retournent pas tout à fait la même chose !

Afficher la doc d’un package:

help()

Sur les liste, dico…

Parcours d’une liste en une seule ligne, c’est en ça que python devient fort et on crée une nouvelle liste, ceci se nomme liste en intention ou compréhension de liste:

[nb * nb for nb in liste_origine]

Mais on peut également introduire un teste des valeurs dans cette opération:

[nb for nb in liste_origine if nb % 2 == 0]

On peut vraiment faire des trucs puissants avec les listes en intention:

[str(round(355/113, i)) for i in range(1, 6)]
    donne : [’3.1’, ’3.14’, ’3.142’, ’3.1416’, ’3.14159’]

Range syntaxe: range(0,10,2) paramètres : debut, fin,pas

slice:

Slice
    L[4:16]
        prend tous les termes de 4 à 15
            terme de droite exclu
            formée des éléments L[k] où k vérifie i≤k<j
    [-4:]
        permet d'avoir les 4 dernier items d'une liste
        C'est vrai aussi pour les chaine de caractères
            texte[-1] permet d'avoir le dernier caractère
    [:5]
        les 5 premiers
    [5:]
        Du 5 ième à la fin
    [4:24:3]
        de 4 à 23 par pas de 3
    [::-1]
        retourne la liste
        s == s[::-1]
            détection de palindrome ;-)
            ça doit être bien utile

any et all sur une liste

any peut servir à faire un OU : any([1,0,1,0,1])

all peut servir faire un ET : all([1,0,1,0,1])

Avertissement

all retourn vrai sur une liste vide

Any et all sont des fonction Python qui s’appliquent sur des itérables (pas forcément des listes)

bit bise
    N << nbits tout simplement
tri avec la fonction sorted
    Il s'agit d'une fonction <strong>builtin</strong>, c'est-à-dire qu'elle est disponible d'office dans Python sans avoir besoin d'importer quoique ce soit.
    accepte des arguments : keys et order
        sorted(etudiants, key=lambda etudiant: etudiant.age, reverse=True)
        remarquer le paramètre de key qui attend une fonction et lambda
    Module operator
        Le module operator propose les fonctions itemgetter et attrgetter qui peuvent être très utiles en tant que fonction clés, si on veut trier une liste de tuples ou une liste d'objets selon un attribut ;
    une autre façon de trier est d'utiliser la méthode sort de la clas list

Aficher un dictionnaire ligne à ligne

for k,v in d.items(): print(« {} : {} ».format(k,v) )

Multiplication de liste

si x est une liste : x * 5 donne une liste qui recopie 5 fois la liste x:

[1,2] * 5 donne [1,2,1,2,1,2,1,2,1,2]
mais [ [1,2] for i in range (3)] donne une liste de 3 listes [[1, 2], [1, 2], [1, 2]]

byte et bytearray

byte est immutable
bytearray est la version mutable
byte(array).fromhex('ABF0 F623').hex('-')
doc pdf <../03-Cours_Docs/programmation/Python/python-3.9.0-docs-pdf-a4/docs-pdf/library.pdf>
bytearray.extend(autre bytearray)
    ou +=
list(bytearray) donne une liste de nombre
bytearray(list)
byarray(int.to_bytes(4, byteorder='big') )

Sur les modules, package…

savoir si un package est importé
    dir()
install package
    dans : C:\Python34\Scripts
        commande pip
            pip install C:\MountWD\00-Outils\06-ConceptionDeveloppement\Python\six-1.9.0-py2.py3-none-any.whl
diff entre import os et from os import *
    dans le premier on est obligé de mettre os.fonction()
    dans le second cas les fonctions font parties de l'espace de noms courant.
    Mais quand il s'agit d'un package avec des sous package ?
        from PyQt5.QtWidgets import QApplication,QWidget
            Par cette instruction on greffe QApplication et QWidget à l'espace de nom local ci bien que l'accès à leur élémentsera un peu plus court au lieu de PyQt5.QtWidgets.QApplication.styleSheet() on écrira QApplication.styleSheet()
            on pourrait aussi faire import PyQt5.QtWidgets.QApplication as QApp et faire QApp.styleSheet
        différence entre ces 2 syntaxes
            from serial.tools import list_ports
                greffe list_ports sur l'espace de nom local
                list_ports.comports()
                si on veut greffer tout le contenu de lit_ports sur l'espace de nom loval on fait
                    from serial.tools.list_ports import *
            import serial.tools.list_ports
                utilisation de la seule fonction de list_ports
                    serial.tools.list_ports.comports()
                Cette instruction import également serial et tools
    import packageName
        n'importe que l'espace de nom : packageName et le contenu de __init__.py
force import
    essayer reload(module)
    import importlib
    importlib.reload()

Relative import

J’ai eu ce problème avec un module dans un sous répertoire de mon appli qui voulait importer un fichier de constante au niveau juste au dessus.

J’ai longuement chercher et il ne semble pas y avoir de solution très élégante (29/6/2022)

Le plus complet que j’ai trouvé : Relative imports in Python 3 sur Stackoverflow [25]

Mais c’est tout de même pas trivial

Sur Les fichiers

__file__
    se dit dunder file ;-)
os.path.dirname(__file__)
    dans le même style:
    os.path.join(dir, 'data', filename)
        dans la doc de reference library.pdf <../03-Cours_Docs/programmation/Python/python-3.9.0-docs-pdf-a4/docs-pdf/library.pdf>
            chapitre "FILE AND DIRECTORY ACCESS"

Ouvrir un fichier avec with:

try:
    with open(fIn, 'r') as f:
        file_content = f.read()
        print "read file " + fIn
    if not file_content:
        print("no data in file " + fIn)
        pass
    except IOError as e:
        print("I/O error({0}): {1}".format(e.errno, e.strerror) )

Autres astuces

event driven dans Tkinter
    on peut ajouter des event grace aux méthodes communes
        ok mais ? comment
copie d'objets
    soit:
obj_a = [1, 4, 5]
obj_b = obj_a
            obj_b n'est pas une copie de obj_a
                les 2 référence le même objet
            alors que dans :
obj_b = list(obj_a)
                obj_b est bien une recopie de obj_a
            on peut utilisé aussi le slicing pour réaliser une vraie copie
                a=b[:]
    Initialisation multiple
        c'est pas a,b,c = 0
        c'est a=b=c=0
        Par contre attention avec les liste
            A=B=C=[1,2,3]
            une seule liste existe et A B et C en sont des alias
    Fonctions : object classique en python
        >>> def add(x, y):
...     return x + y
>>> def sub(x, y):
...     return x - y
>>> def apply(func, x, y): # 1
...     return func(x, y) # 2
>>> apply(add, 2, 1) # 3
3
>>> apply(sub, 2, 1)
1

    operateur ternaire <https://python.developpez.com/cours/DiveIntoPython/php/frdiveintopython/power_of_introspection/and_or.php>
        particularité des opérateur and et or

    permutter 2 varibles
        a,b = b,a
    Connaître son environnement
        object os.environ
            object iterable
            on peut écrire : os.environ['PATH']
                retorune une chaine
    Les décorations d'un script exécutable:
        # -* coding : Latin-1 -* import os #... os.system("pause")
        Mettre fenêtre en pause
            import os
....
os.system("pause")
        if __name__ == "__main__":         #code à executer
        #! /usr/bin/env python3 # -*- coding: utf8 -*-

Ctrl C

#!/usr/bin/env python
import signal
import sys

def signal_handler(sig, frame):
    print('You pressed Ctrl+C!')
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
signal.pause()

Trouvé sur stackoverflow.com How do I capture SIGINT in Python? [26]

autre façon meilleur et testée sur projet IOTEps:

from threading import Event

exit = Event()

def main():
    while not exit.is_set():
    do_my_thing()
    exit.wait(60)

    print("All done!")
    # perform any cleanup here

def quit(signo, _frame):
    print("Interrupted by %d, shutting down" % signo)
    exit.set()

if __name__ == '__main__':

    import signal
    for sig in ('TERM', 'HUP', 'INT'):
        signal.signal(getattr(signal, 'SIG'+sig), quit);

    main()

Sur Stackoverflow break/interrupt a time.sleep() in python [27]

Python2 to 3

Cheat Sheet: Writing Python 2-3 compatible code [28]

Time et dateTime

package standard (pas besoin de pip install)

Construct a file name with time:

from time import strftime
filename= "bprefixe_" + strftime("%Y%m%d-%H%M%S") + ".txt"

Temps utc:

from datetime import datetime
date = int(datetime.now().timestamp())

Afficher une date:

datetime.now().strftime('%d/%m/%Y, %H:%M')
from datetime import timedelta
from datetime import datetime

t1=datetime.now().strftime('%d/%m/%Y %H:%M:%S')
t2=(datetime.now()+timedelta(seconds=60)).strftime('%d/%m/%Y %H:%M:%S')
print(f"t1:{t1} - t2 : {t2}")

I2C

En pré-ambule hors Python:

sudo apt-get install i2c-tools
sudo i2cdetect -y 1

ça sent bon la Raspberry pi ;-)

2 façon de faire smbus ou mieux smbus2 [29] est compatible python 3.x:

pip install smbus2

from smbus2 import SMBus

Sur Pypi smbus2 [30]

et Quick2wire en Python3 [31]

Wiring pi

C’est une librairie C non Python cf.

PySerial

pyserial

Pas de doc pdf seulement doc en ligne [32] mais un très bon readthedoc à noter que la doc sur pythonhosted.org est identique.

pySerial includes a small console based terminal program called Miniterm. It can be started with:

python -m serial.tools.miniterm <port name> (use option -h to get a listing of all options).

import serial et pas pyserial

Utilisation de la classe Serial du module serial:

ser=serial.Serial()
ser.baudrate=19200
ser.port='COM4'
ser.open()

ser.inWaiting() :caractères en attente de réception

Astuce:

ser=serial.Serial()
ser (dans la console python) permet de voir les paramètres et l'état ouvert/fermé
Ecrire une chaîne ser.write( "texte".encode() )

On peut aussi donner tous les paramètres d’un coup au constructeur Serial. Voir la doc short intro [34]

Frames and protocols for the serial port - in Python [33]

Lister les port série

import serial.tools.list_ports
serialPorts = serial.tools.list_ports.comports()

for port, desc, hwid in sorted(serialPorts):
    try:
        print("Essaie port : " + port)
        ser.port = port
        ser.open()
        valideSerials.append( {'port': port, 'description': desc } )
        ser.close()
    except:
        pass

Appli minimum (template)

Construire ou récupérer un set de template. Appli mini en version avec objet/sans objet avec/sans Tkinter au total 4 templates.

J’ai déjà un template avec Qt dans:

\008_iao_wrk\Python\experimentations\appliMiniPyQt

Template à base de tkinter en cours de construction (au 30/6/22):

perso\0044-Iot_ESP_PPlug\projet\_3_software\pythonTools\pytemplt

Snippets

Chemins

import os

print('basename:    ', os.path.basename(__file__))

dir = os.path.dirname(os.path.abspath(__file__))
print('dirname:     ', dir )

fichier = 'nomDuFichier.ext'

fullDir = os.path.join( dir, fichier)
print( "fulldir = ", fullDir )

if os.path.exists(fullDir):
    print('fichier trouvé')

Advenced Python

On commence par cet article qui se ramifie vers pleins de sujets intéressants.

venv

Présentation

Permet de créer un environnement virtuel Python. Ce qui permet de ne pas pourrir ses packages globaux avec des choses indésirées ou des effets de bord.

Usages et limites

On ne déplace pas et on ne copie pas un environnement virtuel et pip show. En général, il est spécifique à une machine donnée ou tout au mois à un arborescence. sauf si on ne bosse qu’avec des chemins génériques comme C:\Program Files\Python312\ en effet le fichier :

pyvenv.cfg garde une trace de l’interpéteur utilisé lors de la création y compris de son chemin, comme par exemple: home = C:\Users\userName\AppData\Local\Programs\Python\Python38-32. On note ici un des inconvénients d’une install dédié à un seul utilisateur ou des install dont on change le nom par défaut.

Création

python -m venv .venvp (ou un autre nom)

Avec cette commande, on a le python par défaut de la machine. Si on en veut un autre, il faut tout d’abord l’intaller (dans ce cas attention à la modification des variables d’environnement) puis donner le chemin complet lors de la création de l’environnement virtuel.

pip commandes essanielles

pip show package

pip freeze

pip list

pip install -r req.txt