Débuter en Python

module assuré par Matias Guijarro

email:

guijarro@esrf.fr

 

Grenoble, France

Introduction

Historique du langage Python

Caractéristiques essentielles

Différentes implémentations de Python

1

Présentation du langage

2

3

Installation

Premiers pas

Problématiques liées à Python

Utilisation d'une distribution de Python

Gestion d'environnements

Eléments de base du langage

Exceptions

Built-in Super Heroes

Paquets et modules

 

1. Présentation du langage

2. Installation

3. Premiers pas

Introduction

#4 au classement TIOBE (Août 2018)

#1 classement IEEE

Plus grande progression sur StackOverflow, numéro 1

1. Présentation du langage

2. Installation

3. Premiers pas

Historique du langage Python

Introduction

janvier 1994

version 1.0

ajout de lambda, map, filter, reduce

(inspiré de Lisp)

février 1991

version 0.9.0

publication du code sur alt.sources (newsgroup)

 

octobre 1996

version 1.4

ajout des keyword arguments,

nombres complexes,

membres de classe "privés"

avril 2001

version 2.1

list comprehensions (inspiré de Haskell), imbrication de la portée des variables, création de la Python Software Foundation

générateurs, unification des types: langage objet complet

version 2.2

décembre 2001

septembre 2006

version 2.5

Gestion de contexte (mot-clef "with")

décembre 2008

version 3.0

Ré-écriture complète, perte de la compatibilité descendante

Dernière de la série 2

version 2.7

juillet 2010

mars 2014

version 3.4

ajout d'asyncio (concurrence d'entrées/sorties sur un seul thread)

juin 2018

version 3.7

Auteur principal: Guido Van Rossum

1. Présentation du langage

2. Installation

3. Premiers pas

Caractéristiques essentielles

Introduction

Langage multi-paradigme

impératif, structuré, fonctionnel, objet

Langage interprété

VM (bytecodes), pas de compilation

Typage dynamique fort

Duck-typing, types objets

Lisibilité du code

Syntaxe épurée, indentation obligatoire

"Batteries included"

Librairie standard très complète

Ramasse-miettes (GC)

Gestion de mémoire automatique

1. Présentation du langage

2. Installation

3. Premiers pas

Différentes implémentations de Python

Introduction

Interpréteur Python écrit en Java, s'intègre à la JVM, permet l'interopérabilité entre Java et Python (depuis 1997)

Interpréteur Python écrit en C#, s'intègre au framework Microsoft .NET (depuis 2006)

Interpréteur Python écrit en... Python, qui s'exécute à travers un compilateur Just-In-Time (depuis 2008)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Problématiques liées à Python

  • De multiples versions de Python existent (Python 2, Python 3, Jython, Pypy, etc.)
    • Différents projets peuvent nécessiter différentes versions
  • On ne peut pas toujours utiliser le Python fourni par le système
    • Mise à jour impossible
    • Package(s) manquant(s)
  • Gestion des dépendances compliquée
    • Les modules Python compilés (en C, C++) peuvent nécessiter des librairies à installer dans le système
    • Conflits potentiels à gérer

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Utilisation d'une distribution de Python

Indépendante du système, pas de conflits entre versions de packages Python, utilisable sans privilèges utilisateur spécifiques

Permet de gérer plusieurs environnements, pour rassembler toutes les dépendances d'un projet dans un même contexte

 

Certaines distribution de Python sont basées sur un package manager complet, pouvant permettre l'installation d'autres logiciels

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Liens vers les archives d'installation pour Linux, Windows et Mac

Utilisation d'une distribution de Python

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Gestion d'environnements

Création conda create -n nouvel_env python=3
Activation Depuis Linux: conda activate nouvel_env
Liste des packages conda list (depuis un environnement actif)
Vers un fichier: conda list --explicit >fichier.txt
Clonage conda create --clone source_env --name nouvel_env
Supprimer conda env remove --name nom_env
Installer un paquet conda install nom_paquet
Exemples:
    > conda install numpy
    > conda install git
Lister les environnements conda env list

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Installer Jupyter

conda create -n turtle python=3
conda activate turtle
conda install -c conda-forge notebook

jupyter notebook

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Python REPL

(Read-Eval-Print Loop)

L'interpréteur Python, démarré sans arguments, propose une invite de commande en mode interactif

 

L'interpréteur attend une ligne en entrée, l'évalue et affiche le résultat de cette évaluation:

matias@kashyyyk ~ $ python
Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1+2
3
>>> print("Hello, world !")
Hello, world !
>>>
matias@kashyyyk ~ $ 

< [CTRL]+[D] permet de quitter l'interpréteur

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Interpréter un fichier

et revenir au mode interactif

matias@kashyyyk ~ $ cat test.py
a = 5

print("Hello, world !")

matias@kashyyyk ~ $ python -i test.py
Hello, world !
>>> a
5
>>> 

L'option de ligne de commande -i permet d'interpréter un fichier et de revenir au mode interactif: les variables et fonctions définies restent disponibles

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Eléments de base du langage

  • Arithmétique: +, -, *, /, //, %, **
  • Programmation conditionnelle: if, else, elif
  • Types de données: int, float, complex, strings, (), [], {}, {:}
  • Objet "vide" (absence de valeur): None
  • Boucles: for, while
  • Modules: import
  • Gestion d'erreurs par exceptions: try...except

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Orientation Objet

  • Tout est objet en Python
    • chaque objet est référencé (refcount)
    • chaque objet a un type de base (classe)
    • tout code Python doit être orienté objet

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Variables et espaces de noms

Les variables n'ont pas besoin d'être déclarées

 

On affecte un objet à une variable en utilisant le signe =

 

Le type des variables est celui de leur valeur

 

Les variables sont donc des références d'objet, qui lient un nom avec un objet dans un espace de nom (namespace)

 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Variables et espaces de noms

Namespace des built-ins du langage: print, range, open...

Module: namespace global

Fonction: namespace local

  • Chaque fonction créé un espace de nom local
  • Les fonctions peuvent être imbriquées

Résolution de nom vers le namespace plus global

La portée des variables dépend des espaces de noms

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Définir une fonction: le mot-clef 'def'

Le mot-clef return permet de retourner une valeur (optionnel)

def nom_de_la_fonction(argument1, ..., argumentN):
    # indentation de début de bloc

Arguments:

  • On peut donner des valeurs par défaut aux arguments
  • La notation * permet de spécifier une liste d'arguments variables
  • La notation ** permet de spécifier un dictionnaire d'arguments variables nommés

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exemples de fonctions

# exemple de fonction avec 2 arguments obligatoires x et y
def addition(x, y):
    # entrée dans le bloc de la fonction:
    # indentation obligatoire
    #
    # la fonction définit un namespace local,
    # la portée des variables x et y ne dépasse
    # pas la fonction
    return x+y

# exemple de fonction avec 2 arguments obligatoires
# et un par défaut
def creer_point(x, y, z=0):
    # retour de valeurs multiples
    return x, y, z

# appel de fonction
>>> creer_point(1, 2)
(1, 2, 0)

>>> creer_point(-1, 2, 3)
(-1, 2, 3)
# exemple de procédure
# (fonction sans valeur de retour)
def afficher_bonjour(nom, prenom):
    print("Bonjour, "+prenom+" "+nom)
    # pas de return: la fonction renvoie
    # None par défaut

      
# exemple d'arguments variables
def compter_amis(*amis):
    # 'amis' reçoit la liste des arguments fournis
    return len(amis) # len renvoie la taille d'une liste

>>> compter_amis("Patrick", "Gérard", "Noémie")
3

# exemple d'arguments variables nommés
def afficher_age_amis(**age_amis):
  # age_amis reçoit le dictionnaire des arguments
  # fournis: { nom_argument: valeur }
  for ami, age in age_amis.items():
      print(ami.title()+" a "+str(age)+" ans.")

>>> afficher_age_amis(patrick=38, gerard=60, noemie=27)
Patrick a 38 ans.
Gerard a 60 ans.
Noemie a 27 ans.

Blocs délimités par l'indentation:

Utiliser un éditeur de code qui remplace [Tab] par des espaces,

configurer pour avoir 4 espaces par [Tab]

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Entrées / Sorties

Réaliser une saisie: fonction input

>>> n = input("Quelle est la réponse ? ")
Quelle est la réponse ?

L'exécution attend la saisie de l'utilisateur, terminée par [Entrée]

 

La valeur renvoyée par input est toujours de type string (chaîne de caractères)

Flux standards (stdin, stdout, stderr)

Afficher un texte: fonction print

>>> print("Hello, world !")
Hello, world !
>>> import sys
>>> print("Hello to stderr", file=sys.stderr)
Hello to stderr

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Entrées / Sorties

open permet d'ouvrir un fichier en lecture ou en écriture

dans un fichier

>>> f = open("/etc/issue", "r")
>>> print(f.read())

Linux Mint 17.2 Rafaela \n \l

>>> 
Caractère Description
r Lecture (depuis le début) - par défaut
w Ecriture (fichier existant écrasé)
a Ajouter (depuis la fin)
b binaire
t texte - par défaut

Le mode d'accès est spécifié par une combinaison de caractères

.read(): lit l'intégralité du fichier

.readline(): lit une ligne

.write(s): écrit la chaîne s

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

La Tortue

Module inspiré de la "tortue graphique" (turtle) du langage Logo

 

Permet de dessiner en utilisant une table traçante virtuelle

 

La documentation officielle de Python explique en détails les fonctions du module turtle : suivre ce lien

Installation sous Jupyter notebook

 

 

et redémarrer: jupyter notebook

conda install notebook -c conda-forge

pip install mobilechelonian,

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice T1:

Continuer le script suivant pour écrire une fonction "marche" qui dessine une marche d'escalier, puis une fonction "escalier" qui dessine un nombre de marches donné en paramètres

from turtle import *  # ou bien: from mobilechelonian import Turtle sous Jupyter

t = Turtle()
t.speed(5)              #parametrage de la vitesse de 1 lent à 10 rapide, 0 étant la vitesse la plus rapide
t.shape("turtle")       #choix de la forme de la tortue
t.pencolor("red")       #choix de la couleur du crayon
t.pensize(4)            #épaisseur du crayon
t.up()                  #lever le crayon     
t.goto(0,0)             #aller à la position (0,0)
t.setheading(0)         #orientation de la tortue vers l'Est / 90  Nord / 180 Ouest / 270 Sud
t.down()                #poser le crayon

50 pixels

Indication: en Python normal (pas Jupyter), la fonction "turtle.mainloop" permet de conserver la fenêtre graphique à l'écran à l'issue du tracé

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice T1:

 

from turtle import * 

speed(5)            #parametrage de la vitesse de 1 lent à 10 rapide, 0 étant la vitesse la plus rapide
shape("turtle")     #choix de la forme de la tortue
pencolor("red")     #choix de la couleur du crayon
pensize(4)          #épaisseur du crayon
up()                #lever le crayon     
goto(0,0)           #aller à la position (0,0)
setheading(0)       #orientation de la tortue vers l'Est / 90  Nord / 180 Ouest / 270 Sud
down()              #poser le crayon

def marche():
    left(90)
    forward(50)
    right(90)
    forward(50)

def escalier(n):
    for i in range(n):
        marche()

escalier(5)

mainloop()

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice T2:

 

Ecrire la fonction "polygone" qui dessine un polygone régulier de N côtés

avec N=6

Exemple:

La fonction doit prendre un argument optionnel "longueur" avec une valeur par défaut de 50 pixels

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice T2

 

def polygone(n, longueur=50):
    pendown()
    angle = 360/n
    for i in range(n):
      forward(longueur)
      right(angle)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice T3: l'horloge

 

Ecrire une fonction "horloge" qui affiche une horloge avec le mouvement de l'aiguille des secondes (trotteuse), prenant en paramètre le rayon de l'horloge en pixels (valeur par défaut: 100 pixels)

Indications:

  • Pour faire une animation il suffit d'effacer le dessin existant en repassant en blanc et d'en faire un autre...
  • Le module "datetime" fournit les données relatives au temps
  • Le module "time" possède une fonction "sleep(X)" pour attendre X secondes
  • Utiliser des fonctions imbriquées (= fonctions dans une fonction) pour dessiner le cadran, dessiner l'aiguille, effacer l'aiguille, etc.

 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice T3

 

import time
import datetime
from turtle import *

shape("arrow")
pensize(4)
delay(0)
home()

def horloge(taille_cadran=100):
    def dessiner_cadran():
      angle = 360/12
      penup()
      for i in range(12):
        right(angle)
        forward(taille_cadran)
        pendown()
        dot(taille_cadran/20, "blue")
        penup()
        backward(taille_cadran)

    def dessiner_trotteuse(s):
        angle = 360/60
        setheading(90-(s*angle))
        color("red")
        forward(taille_cadran*0.8)

    def effacer_trotteuse():
        color("white")
        backward(taille_cadran*0.8)

    dessiner_cadran()
    pendown()

    while True:
        now = datetime.datetime.now()
        dessiner_trotteuse(now.second)
        time.sleep(1) # pause de 1 seconde
        effacer_trotteuse()

horloge(200)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 1

Ecrire le programme qui calcule la factorielle d'un nombre entier positif saisi par l'utilisateur

 

factorielle(n) = n*(n-1)*(n-2)*...*1,  notation mathématique: n!

 

A savoir: 0! = 1

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice 1

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

>>> a = input("Veuillez saisir un nombre entier positif: ")
>>> print(fact(int(a)))

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 2

Ecrire la fonction qui extrait et affiche la n-ième fortune du fichier /usr/share/games/fortunes/fortunes

 

Les fortunes sont délimitées par une ligne contenant la chaîne de caractères "%\n"

 

Les fortunes peuvent occuper plusieurs lignes

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice 2

def fortune(n):
    with open("/usr/share/games/fortunes/fortunes") as f:
        i = 1
        while i < n:
            if f.readline() == '%\n':
                i += 1
        l = f.readline()
        while l != '%\n':
            print(l)
            l = f.readline()
        

>>> fortune(10)
Accent on helpful side of your nature.  Drain the moat.

>>>

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Gestion d'erreur par exceptions

Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> bla
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'bla' is not defined

>>> 1/0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> try:
...   1/0
... except ZeroDivisionError:
...   print("Désolé: opération impossible.")
... 
Désolé: opération impossible.
>>> 

Lorsqu'une erreur se produit, Python génère une exception qui interrompt l'exécution en cours

< try...except permet d'attraper les exceptions, pour rétablir l'exécution normale

 >

le type d'exception renseigne sur la  nature du problème

< la trace rappelle le contexte dans lequel l'erreur s'est produite

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Approche optimiste du traitement des erreurs

On essaie le cas général, on traite éventuellement les exceptions

 

"EAFP": Easier to Ask Forgiveness than Permission

import os

# ne correspond pas au style de codage EAFP
if os.path.exists("file.txt"):
    os.unlink("file.txt")
import os

try:
    os.unlink("file.txt")
# OSError est levée si le fichier n'existe pas
except OSError:
    # on ignore l'erreur, dans ce cas
    pass

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Levée d'exception

Le mot-clef raise permet de générer une exception dans son propre code

# calcul du périmètre d'un cercle,
# le rayon ne doit pas être négatif
def périmètre(rayon):
    if rayon < 0:
        raise ValueError("Le rayon du cercle est négatif")
    return 2*rayon*math.pi

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Masquer les exceptions

A moins de bien savoir, masquer les exceptions s'effectue en utilisant la classe Exception

try:
    ...
except Exception as e:
    if isinstance(e, ZeroDivisionError):
        # division par zero
    elif isinstance(e, AttributeError):
        # attribut manquant
    ...
# l'exécution du code continuera toujours ici ensuite
# (à moins que le code plus haut ne fasse un return...) 

La clause except: utilisée sans spécifier de type d'exception rattrape TOUTES les exceptions (y compris KeyboardInterrupt, par exemple)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Clauses "else" et "finally"

Le schéma classique try...except: accepte une clause else: qui est exécutée quand il n'y a pas d'erreur

 

Enfin, la clause finally: est toujours exécutée (y compris en cas d'exception dans le traitement de l'exception)

 

Cela est particulièrement utile pour libérer une ressource (lock, fermeture de fichier, ...)

try:
    ... #code pouvant générer une exception
except Exception:
    ... #code de traitement des exceptions
else:
    ... #code exécuté s'il n'y a pas eu d'exception
finally:
    ... #code TOUJOURS exécuté à la fin

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Built-ins Python

Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 
'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError',
'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception',
'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError',
'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError',
'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError',
'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError',
'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError',
'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError',
'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', 
'__build_class__',
'__debug__', 
'__doc__', 
'__import__', 
'__loader__', 
'__name__', 
'__package__', 
'__spec__', 
'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod',
'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float',
'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance',
'issubclass', 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open',
'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted',
'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
>>> 

Exceptions standard

Fonctions

Connaître l'essentiel des fonctions standard du langage (et à quoi elles servent !) et les exceptions les plus courantes par coeur est un atout

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

int

float

Entier signé, 64 bits minimum

Taille maximum infinie, limité à la mémoire disponible

 

Décimal signé, 64 bits minimum

(voir sys.float_info)

>>> n = 26  # == 0x1A, == 032, == int('26')
>>> 1234567**89
13957418598822621635241167796707938465481984
07702631541489010157114345875559162218718550
53939370772085656915237689603767083349991793
71334647109157262849410607614641938921108745
92091708239393326778590937567344701723518950
79430772488109105692648444480028380122417140
93256943282431643000874095802505371673542134
47294105206815597154794725416313214250137689
78692403188995380725532383634472359562908542
46954797114645706242235684570157179581172114
61266397348099045779670126089615289970121785
37266420111446716001493476975934877110973383
995456863730247
>>> f = 26.4  # == 2.64E1 == float('26.4')
>>> import sys
>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308, 
max_exp=1024, max_exp=308, 
min=2.2250738585072014e-308, 
min_exp=-1021, min_10_exp=-307, dig=15, 
mant_dig=53, epsilon=2.220446049250313e-16, 
radix=2, rounds=1)
>>> 0.9999999999999999 #plus de 15 chiffres
1.0

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

str, bytes

Chaînes de caractères

s1 = 'Hello'
s2 = 'β is b in greek' # une string Python peut contenir des caractères Unicode
s3 = '12'
s3 == str(12)

Les chaînes de caractères Python sont immutables

>>> s='ABC'
>>> s[2]='*'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> 
b1 = b':)' # représentation de l'entier 14889 (codée sur 2 bytes; Big-Endian)
b1 == bytes((58,41))

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

[ ]

Conteneurs:

liste

Séquence ordonnée mutable

>>> l1 = [1, 2.5, None, "hello"]
>>> l1[0] == 1
True
>>> l1[-1] == "hello"
True
>>> l1.index(None) == 2
True
>>> del l1[2]
>>> len(l1) == 3
True
>>> 
# créer une liste de 1000 zéros
l2 = [0]*1000

# créer une liste issue d'une séquence
# de caractères
l3 = list("hello")
l3 == ["h", "e", "l", "l", "o"]

# créer une suite avec "range()"
l4 = range(10)
l4 == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

( )

Conteneurs:

tuple (n-uplet)

Séquence ordonnée immutable

>>> t1 = ("Pierre", "Martin", 24)
>>> nom, prenom, age = t1         # dégroupage
>>> t1[0] == prenom == "Pierre"
True
>>> t1[-1] == 24
True
>>> prenom, nom = nom, prenom    # permutation
>>> prenom == "Martin"
True
>>> del t1[2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
>>> 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

{ }

Conteneurs:

set

Ensemble d'éléments uniques non-ordonné, mutable

>>> filles = set(('Julie', 'Alice'))
>>> garçons = set(('Romain', 'Pierre'))
>>> personnes = filles | garçons         # '|': opérateur d'union
>>> personnes
{'Romain', 'Julie', 'Alice', 'Pierre'}
>>> vegetariens = set(('Pierre', 'Julie'))
>>> filles_vegetariennes = filles.intersection(vegetariens)
>>> filles_vegetariennes
{'Julie'}
>>> personnes_qui_mangent_de_la_viande = personnes - vegetariens
>>> personnes_qui_mangent_de_la_viande
{'Romain', 'Alice'}
>>> 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

{:}

Conteneurs:

dict

"map", "dictionnaire": association d'une clef et d'une valeur ; mutable

>>> d = { "a": 5, "b": 4, "toto": [1,2,3], 8: "hello" }
>>> d["a"] == 5
>>> d[8] == "hello"
>>> list(d.keys())
['a', 'b', 'toto', 8]
>>> list(d.values())
[5, 4, [1, 2, 3], 'hello']
>>> del d["b"]
>>> len(d) == 3
True

< depuis Python 3.7, l'ordre d'insertion des données dans un dict est préservé pour le parcours des clefs ou des valeurs

non-ordonné avant Python 3.7

Modern dictionaries: présentation de R. Hettinger

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

bool

Vrai / Faux logique

b1 = True  # == 1
b2 = False # == 0
bool True False
0
1234
-10
"False"
""
None
()
[]
{}
[1, 6, 9]

Tout type de donnée ayant une valeur "vide" est Faux

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Opération utiles sur les chaînes de caractères

>>> s=" Hello, world ! "
>>> s.lower() # conversion en minuscules
" hello, world ! "
>>> s.upper() # conversion en majuscules
" HELLO, WORLD ! "

>>> s.split(", ") # renvoie une liste, en découpant la string suivant le délimiteur spécifié
[" Hello", "world ! "]
>>> "-".join(["a","b"]) # concaténer les éléments d'un itérable
"a-b"
>>> s.replace(" !", ".") # remplacer une sous-chaîne par une autre
" Hello, world. "
>>> s.strip() # supprimer les espaces avant et après la chaîne
"Hello, world !"

Formatage et templating

>>> s = "Je m'appelle {} {}.".format("Matias", "Guijarro")
>>> print(s)
Je m'appelle Matias Guijarro.
>>> name = "Matias"
>>> s = f"Je m'appelle {name}" # nouveau dans Python 3.6: f-strings
>>> print(s)
Je m'appelle Matias
>>> a, b = 3, 5
>>> s = f"{a} + {b} = {a+b}"
>>> print(s)
3 + 5 = 8

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

while

Boucle  simple:

Exécute le code dans le bloc tant que la condition est vraie

>>> i = 0
>>> while i < 5:
      print(i)
      i += 1
0
1
2
3
4
>>>

Le mot-clef break permet de sortir d'une boucle sans que la condition ne soit évaluée

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

for

Boucle d'itération

>>> for item in container:
        ...
>>> for i, item in enumerate(container):
        # i est l'index de l'item dans le container
        ...
>>> for item1, item2 in zip(container1, container2):
        # item1 est le n-ième item de container1
        # item2 est le n-ième item de container2
        # (item1 et item2 doivent avoir la même longueur)
        ...
>>> for item in reversed(container):
        # les items sont retournés en commençant par la fin
        ...
>>> for item in sorted(container):
        # les items sont retournés triés
        ...
# Somme des valeurs d'un type conteneur
# exemple avec une liste d'entiers:
>>> sum([1,2,3])
6
# Valeur maximum
>>> max([1,2,3])
3
# Valeur minimum
>>> min([1,2,3])
1
# 'all' retourne True si toutes les valeurs du conteneur sont vraies
>>> all(container)
# 'any' retourne True si l'une des valeurs du conteneur est vraie
>>> any(container)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Compréhensions (ou intensions)

Boucles d'itération plus performantes

Compréhension de liste

liste_resultat = [ expr for item in itérable if cond ]

Compréhension d'ensemble

set_resultat = { expr for item in itérable if cond }

Compréhension de dictionnaire

dict_resultat = { k:v for k,v in itérable if cond }

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Quizz

 

Comment vérifier qu'un conteneur est vide ?

if len(container) == 0:    # 1 point
   

if container:              # 5 points :-)
   

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Quizz

 

Comment vérifier qu'un élément est dans une liste ?

inside = False              # -1 point
for v in my_list:
    inside = v == item
    if inside:
        break

inside = item in my_list    # 5 points :-)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Quizz

 

Comment initialiser les valeurs d'un dictionnaire

(cas uniforme) ?

counts = {}               # 1 point
if item not in counts:
    counts[item] = 0
counts[item] += 1
              
from collections import defaultdict
counts = defaultdict(int)
counts[item] += 1        # 5 points :-)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 3

En utilisant des comprehensions:

  • Trouver tous les nombres divisibles par 7 entre 1 et 1000
  • Trouver tous les nombres de 1 à 1000 qui ont un seul "3" dedans
  • Ecrire une fonction count_spaces pour compter les espaces d'une chaîne de caractères
  • Renvoyer les consonnes de la phrase "The quick brown fox jumped over the lazy dog"
  • Ecrire une fonction list_4_letter_words qui renvoie la liste des mots de 4 lettres d'une phrase donnée

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice 3


# nombres divisibles par 7 entre 1 et 1000

print([n for n in range(1,1001) if n%7 == 0])

# les nombres entre 1 et 1000 avec un seul 3 dedans

print([n for n in range(1,1001) if str(n).count('3')==1])

# count_spaces

def count_spaces(sentence):
    return sum([1 if x.isspace() else 0 for x in sentence])

assert count_spaces("The quick brown fox jumps on the lazy dog")==8

def count_spaces(sentence):
    return sentence.count(" ")

assert count_spaces("The quick brown fox jumps on the lazy dog")==8

# consonnes

consonnes = "".join([c if c not in 'aeiouy' else '' for c in "The quick brown fox jumps on the lazy dog"])
print(consonnes)

# liste des mots de 4 lettres

def list_4_letter_words(sentence):
    return list(filter(None, [w if len(w)==4 else None for w in sentence.split(" ")]))

assert list_4_letter_words("The quick brown fox jumps on the lazy dog")==["lazy"]

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 4

Traduire le R2D2 dans le texte

Récupérer le transcript du message de R2D2 ici

Grouper les 0 et les 1 par 8 pour former des octets, décoder en utilisant la table ASCII, afficher le message

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice 4

msg = '01110011001000000110111001101111001000000010000001101001001000000111001101101110001000000110010100100000001000000110100000100000001000000110010100100000011100100010000000100000011100000110110100100000011011110010000001100011'

octets = [msg[i:i+8] for i in range(0, len(msg), 8)]

chars = [chr(int(c, 2)) for c in octets]

removed_spaces = list(filter(None, [c.strip() for c in chars]))

decoded_msg = "".join(reversed(removed_spaces))

print(decoded_msg)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 5: Jedis et sabres laser

Compter les différentes couleurs de sabres laser

-- lien vers la ressource ici

 

Sortie attendue:

{'green': 6, 'red': 5, 'blue': 6}

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice 5

# avec le module 'collections'


frequencies = collections.Counter(jedi['lightsaber_color'] for jedi in jedis)


# ou bien avec une comprehension de dictionnaire

colors = [jedi['lightsaber_color'] for jedi in jedis]
frequencies = {color: colors.count(color) for color in set(colors)}



print(frequencies)
{'green': 6, 'red': 5, 'blue': 6}

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Paquets et modules

  • Un module est un fichier .py contenant du code Python (définitions, déclarations et expressions exécutables)
    • Permet de grouper le code d'un même sous-problème
    • Organiser le code facilite le débuggage et le rend plus facile à comprendre
  • Un paquet est un ensemble de modules formant une librairie Python

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Anatomie d'un module Python

# -*- coding: utf-8 -*-




import <module1>
from <module2> import <nom>


VARIABLE_GLOBALE = <valeur>




def fonction_quelconque(arg1, arg2, ...):
    <code de la fonction>

...

class MaClasse(object):
    ...



if __name__ == '__main__':
    ...

< Déclaration de l'encodage du fichier (ISO 10646, compatible ASCII, le plus utilisé)

Optionnel pour utf-8: encodage par défaut

< Importation des dépendances (modules auxiliaires)

< Définition des variables globales

< Définition des fonctions et des classes du module

< Code exécuté uniquement quand l'interpréteur lit le module en tant que script (pas importé depuis un autre module)

mon_module.py

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Définition d'une fonction de multiplication f(x,y)=>x*y, dans un module appelé "test.py"

# -*- coding: utf-8 -*-

def f(x, y):
    return x*y

test.py

Utilisation:

(exercices) matias@kashyyyk ~ $ python
Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import test
>>> test.f(5,6)
30
(exercices) matias@kashyyyk ~ $ python
Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from test import f
>>> f(5,6)
30
>>> f("a", 8)
'aaaaaaaa'
>>> 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Les modules sont des objets comme les autres

  • Une fois importé un module Python devient un objet dans le système
    • stocké dans sys.modules
  • Les objets modules constituent des espaces de noms (namespaces)
  • On peut manipuler les propriétés d'un objet module
(exercices) matias@kashyyyk ~ $ python
Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys #module built-in de Python
>>> import test #notre module de test
>>> print(sys.modules["test"])
<module 'test' from '/tmp/test.py'>
>>> test.toto=42
>>> print(test.toto) # la fonction built-in 'print' affiche un résultat
42
>>> dir(test) # la fonction built-in 'dir' affiche les membres d'un objet
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'f', 'toto']
>>> 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Les modules sont des singletons

  • une seule instance d'un même module existe dans un programme Python
  • elle est partagée entre tous les modules qui l'importent
import test

# 'id' retourne l'adresse d'un objet en mémoire
adresse_memoire_de_test = id(test)

test2.py

import test

# 'id' retourne l'adresse d'un objet en mémoire
adresse_memoire_de_test = id(test)

test3.py

(exercices) matias@kashyyyk ~ $ python
Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import test2
>>> import test3
>>> import test
>>> id(test) == test2.adresse_memoire_de_test == test3.adresse_memoire_de_test
True

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Utilisation du module "test" en tant que script Python


# -*- coding: utf-8 -*-

def f(x, y):
    return x*y

if __name__ == '__main__':
    print(f(5, 6))

test.py

Utilisation:

(exercices) matias@kashyyyk ~ $ python /tmp/test.py
30

exécutable (sous Linux)

< ajout du "shebang"

#!/usr/bin/env python
(exercices) matias@kashyyyk ~ $ chmod +x /tmp/test.py
(exercices) matias@kashyyyk ~ $ /tmp/test.py
30
 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Paquet ("package") Python

matias@kashyyyk ~/dev/exemple_paquet $ tree
.
├── test.py
├── bla.py
├── __init__.py
└── toto
    ├── __init__.py
    └── blabla.py

Ensemble de modules dans une arborescence constituant une librairie

< Module membre du paquet

< fichier __init__.py (peut être vide), permet à Python de reconnaître que le répertoire fait partie d'un paquet

 sous-répertoire du paquet >

Utilisation:

(exercices) matias@kashyyyk ~/dev/exemple_paquet $ cd ..
(exercices) matias@kashyyyk ~/dev $ python
Python 3.7.0 (default, Jun 28 2018, 13:15:42) 
[GCC 7.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from exemple_paquet import test
>>> test.f(5,6)
30
>>> 

< Python recherche les paquets dans le répertoire courant et dans ceux définis par la variable d'environnement PYTHONPATH

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 6a: petit script de multiplication

Ecrire un script "test.py" qui prend un nombre variable de nombres en ligne de commande et qui utilise une fonction "multiply" pour afficher le resultat de la multiplication de ces nombres par eux-memes

python -c "import test; print test.multiply(2,3)"
    ---> sortie 6.0
  
python test.py 2 1.5
    ---> sortie 3.0

python test.py 2 3 4
    ---> sortie 24

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 6a: Correction

import sys 
 
def multiply(*n): 
    res = 1 
    for x in n: 
        res *= x 
    return res 
 
if __name__ == '__main__': 
    nombres = [float(x) for x in sys.argv[1:]] 
    print(multiply(*nombres)) 

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 6b: le jeu du Pendu

Ecrire un programme de jeu du Pendu, en piochant les mots dans le fichier dictionnaire du système

 

Proposer à l'utilisateur un mot masqué au hasard, en remplaçant les lettres par "_" et à chaque entrée de caractère vérifier si le caractère figure dans le mot

 

Si oui : l'afficher

Si non: afficher un élément du pendu, si la potence est complète c'est game over !

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exerice 6: Jeu du Pendu,

lien ici

import random
import os
import sys
import unidecode

HANGMAN = ["""
      _______
     |/      |
     |      
     |     
     |      
     |    
     |
    _|___
""",
"""
      _______
     |/      |
     |      (_)
     |     
     |     
     |  
     |
    _|___
""",
"""
      _______
     |/      |
     |      (_)
     |      \|/
     |      
     |     
     |
    _|___
""",
"""
      _______
     |/      |
     |      (_)
     |      \|/
     |       |
     | 
     |
    _|___
""",
"""
      _______
     |/      |
     |      (_)
     |      \|/
     |       |
     |      / L
     |
    _|___
"""]

def find_word():
    word = None
    dictionary_file = os.path.join(os.path.dirname(__file__), "french.txt")
    with open(dictionary_file, encoding='latin-1') as df:
        while True:
            i = random.randint(0, 22739)
            word = next((line.strip() for j, line in enumerate(df) if i==j))
            if word[0].isupper():
                df.seek(0)
            else:
                break
    return word 

def g_display_hangman():
    """Display hangman"""
    for h in HANGMAN:
        yield h+'\n'

def g_ask_for_char():
    already_tried = []
    while True:
        input_char = input("Proposer une lettre: ").upper()
        if len(input_char) > 1:
            continue
        if input_char in already_tried:
            print(f"Deja essaye: {input_char}")
            continue
        already_tried.append(input_char)
        yield input_char 

def display_word(word, found):
    print('\n')
    print("Mot: {}".format(" ".join([c if found[i] else "_" for i,c in enumerate(word)])))
    print('\n')

def start_game(debug):
    game_over = False
    word = find_word()
    assert word is not None
    if debug:
        print(f'Mot a trouver = {word}')
    normalized_word = unidecode.unidecode(word).upper()
    found_chars = [False]*len(word)
    
    display_hangman = g_display_hangman()
    ask_for_char = g_ask_for_char()
    
    print(next(display_hangman))
    while not game_over:
        display_word(word, found_chars)
        input_char = next(ask_for_char)          
        if input_char in word.upper():
            found_chars = [found_chars[i] or input_char==c for i,c in enumerate(normalized_word)]
            if all(found_chars):
                break
        else:
            try:
                print(next(display_hangman))
            except StopIteration:
                game_over = True
    if game_over:
        print(f"Dommage... Le mot etait: {word}")
    else:
        print("Super!")

if __name__ == '__main__':
    debug = False
    if '-d' in sys.argv[1:]:
        debug = True
    start_game(debug)

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution numéro 2 de l'exerice 6: Jeu du Pendu

Pour aller plus loin, avec la librairie graphique Qt

from PyQt5.Qt import Qt, QApplication, QMainWindow, QWidget, QMenuBar, QMenu, QLabel, QVBoxLayout, QHBoxLayout, QMessageBox, QPixmap
import functools
import random

LISTE_MOTS = ["trouver", "ballon", "cyclone", "voiture", "python"]

def generateur_gibet(dessin_pendu):
    hangman = [[
"64 47 2 1 ",
"  c none",
". c #3D3D3D",
"                                                                ",
"                                                                ",
"                                                                ",
"                 .    .  .   .    .                             ",
"              .........................                         ",
"              .                      ..                         ",
"              .                      ..                         ",
"             ..                       .                         ",
"              .                       .                         ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"  .........................................................     ",
"     .   .  .  .  .   .  .   .   .   .    .  .    .  .    .     ",
"                                                                ",
"                                                                "],
[
"64 47 2 1 ",
"  c none",
". c #414141",
"                                                                ",
"                                                                ",
"                                                                ",
"                 .    .  .   .    .                             ",
"              .........................                         ",
"              .                      ..                         ",
"              .                      ..                         ",
"             ..                       .                         ",
"              .                      ..                         ",
"              .                    .......                      ",
"              .                   ..     .                      ",
"             ..                   .  . . ..                     ",
"              .                   . .. .. .                     ",
"              .                  ..    . ..                     ",
"              .                   .  ..  ..                     ",
"             ..                   ..     .                      ",
"              .                    ..  ...                      ",
"              .                     ....                        ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"  .........................................................     ",
"     .   .  .  .  .   .  .   .   .   .    .  .    .  .    .     ",
"                                                                ",
"                                                                "],
[
"64 47 2 1 ",
"  c none",
". c #414141",
"                                                                ",
"                                                                ",
"                                                                ",
"                 .    .  .   .    .                             ",
"              .........................                         ",
"              .                      ..                         ",
"              .                      ..                         ",
"             ..                       .                         ",
"              .                      ..                         ",
"              .                    .......                      ",
"              .                   ..     .                      ",
"             ..                   .  . . ..                     ",
"              .                   . .. .. .                     ",
"              .                  ..    . ..                     ",
"              .                   .  ..  ..                     ",
"             ..                   ..     .                      ",
"              .                    .......                      ",
"              .                      ...                        ",
"              .                       .                         ",
"              .                       .                         ",
"              .                       .                         ",
"             ..                      ..                         ",
"              .                       .                         ",
"              .                       .                         ",
"              .                       .                         ",
"              .                       .                         ",
"              .                       .                         ",
"             ..                      ..                         ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"  .........................................................     ",
"     .   .  .  .  .   .  .   .   .   .   .   .    .  .    .     ",
"                                                                ",
"                                                                "],
[
"64 47 2 1 ",
"  c none",
". c #434343",
"                                                                ",
"                                                                ",
"                                                                ",
"                 .    .  .   .    .                             ",
"              .........................                         ",
"              .                      ..                         ",
"              .                      ..                         ",
"             ..                       .                         ",
"              .                      ..                         ",
"              .                    .......                      ",
"              .                   ..     .                      ",
"             ..                   .  . ....                     ",
"              .                   . .. .. .                     ",
"              .                  ..    .  .                     ",
"              .                   .   .  ..                     ",
"             ..                   .. .   .                      ",
"              .                    .......                      ",
"              ..                     ...                        ",
"              .                       .                         ",
"              .               .  .   ..                         ",
"              .             ...........                         ",
"             ..                      ..                         ",
"              .                       .                         ",
"              .                       .                         ",
"              .                       .                         ",
"             ..                       .                         ",
"              .                       .                         ",
"              .                      ..                         ",
"              .                       .                         ",
"              ..                                                ",
"              .                                                 ",
"              .                                                 ",
"              ..                                                ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"  .........................................................     ",
"     .   .  . .   .   .  .   .   .   .    .  .    .  .    .     ",
"                                                                ",
"                                                                "],
[
"64 47 2 1 ",
"  c none",
". c #444444",
"                                                                ",
"                                                                ",
"                                                                ",
"                 .    .  .   .    .                             ",
"              .........................                         ",
"              .                      ..                         ",
"              .                      ..                         ",
"             ..                       .                         ",
"              .                      ..                         ",
"              .                    .......      ..........      ",
"              .                   ..     .      ..... ...       ",
"             ..                   .  . ....   .                 ",
"              .                   . .. .. .  .                  ",
"              .                  ..    .  . .                   ",
"              .                   .   .  ..                     ",
"             ..                   .. .   .                      ",
"              .                    .......                      ",
"              .                      ...                        ",
"              .                       .                         ",
"              .               .    . .. .   .                   ",
"             ..             ....................                ",
"              .                      ..                         ",
"              .                       .                         ",
"              .                       .                         ",
"             ..                       .                         ",
"              .                       .                         ",
"              ..                      .                         ",
"              .                      ..                         ",
"              .                       .                         ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"              .                                                 ",
"  .........................................................     ",
"     .   .  . .   .   .  .   .   .   .   .   .    .  .    .     ",
"                                                                ",
"                                                                "],
[
"64 47 2 1 ",
"  c none",
". c #444444",
"                                                                ",
"                                                                ",
"                                                                ",
"                 .    .  .   .    .                             ",
"              .........................                         ",
"              .                      ..                         ",
"              .                      ..                         ",
"             ..                       .                         ",
"              .                      ..                         ",
"              .                    .......                      ",
"              .                   ..     .                      ",
"             ..                   .      ..                     ",
"              .                   .       .                     ",
"              .                  ..      ..                     ",
"              .                   .   .  ..                     ",
"             ..                   ..  .  .                      ",
"              .                    .......                      ",
"              ..                     ...                        ",
"              .                       .                         ",
"              .               .    . .. .   .                   ",
"              .             ....................                ",
"             ..                      ..                         ",
"              .                       .                         ",
"              .                       .                         ",
"              .                       .                         ",
"             ..                       .                         ",
"              .                       .                         ",
"              .                      ..                         ",
"              .                       .                         ",
"              ..                     ...                        ",
"              .                     .. ..                       ",
"              .                    ..   .                       ",
"              ..                  ..     ..                     ",
"              .                   .       .                     ",
"              .                  ..       ..                    ",
"             ..                 ..         ..                   ",
"              .                ..           .                   ",
"              .                                                 ",
"              .                                                 ",
"             ..                                                 ",
"              .                                                 ",
"              .                                                 ",
"  .........................................................     ",
"     .   .  . .   .   .  .   .   .   .    .  .    .  .    .     ",
"                                                                ",
"                                                                "]]
    for xpm_data in hangman:
        dessin_pendu.setPixmap(QPixmap(xpm_data))
        yield

def demarrer_pendu(main_window):
    # Zone de jeu en position centrale 
    zone_jeu = QWidget()
    QVBoxLayout(zone_jeu)
    zone_jeu.layout().addWidget(QLabel("<center><h1>Mot à trouver:</h1></center>"))
    main_window.setCentralWidget(zone_jeu)
    hbox = QHBoxLayout(main_window)
    dessin_pendu = QLabel(zone_jeu)
    dessin_pendu.setScaledContents(True)
    dessin_pendu.setFixedWidth(128)
    dessin_pendu.setAlignment(Qt.AlignHCenter)
    hbox.addWidget(dessin_pendu)
    zone_jeu.layout().addLayout(hbox)

    # nouveau mot a trouver
    mot_a_trouver = random.choice(LISTE_MOTS)
    print(mot_a_trouver) #pour le debogage
    mot_cherche = ["_"]*len(mot_a_trouver)
    
    label_mot_a_trouver = QLabel()
    def affiche_mot_cherche():
        label_mot_a_trouver.setText(f"<center><b><h1>{' '.join(mot_cherche)}</h1></b></center>")
    zone_jeu.layout().addWidget(label_mot_a_trouver)

    def tester(char):
        trouve = False
        gagne = False
        for i, c in enumerate(mot_a_trouver):
            if char.lower() == c.lower(): # comparaison en minuscules
                mot_cherche[i] = c
                trouve = True
        gagne = not '_' in mot_cherche
        return trouve, gagne
   
    affiche_gibet = generateur_gibet(dessin_pendu)

    def gerer_appui_touche(event):
        char_code = event.key()
        try:
            char = chr(char_code)
        except ValueError:
            pass
        else:
            trouve, gagne = tester(char)
            affiche_mot_cherche()
            if not trouve:
                try:
                    next(affiche_gibet)
                except StopIteration:
                    QMessageBox.critical(None, "Dommage", "Perdu... :(")
                    main_window.keyPressEvent = main_window.saved_keypressevent
                    zone_jeu.close()
            if gagne:
                QMessageBox.information(None, "Félicitations!", "C'est gagné")
                main_window.keyPressEvent = main_window.saved_keypressevent
                zone_jeu.close()        

    main_window.saved_keypressevent = main_window.keyPressEvent  #sauver la fonction
    main_window.keyPressEvent = gerer_appui_touche #remplacer la fonction par la notre
    affiche_mot_cherche()

if __name__ == '__main__':
    # Creation de l'application
    app = QApplication([])

    # Fenetre principale
    main_window = QMainWindow()
    main_window.setMinimumSize(500,500)
    main_window.setWindowTitle("Jeu du Pendu")
    
    # Barre de menu
    menu = QMenuBar()
    main_window.setMenuBar(menu)
    
    # Menu
    menu_partie = QMenu("Partie")
    menu_partie.addSeparator()
    menu.addMenu(menu_partie)

    action_demarrer = menu_partie.addAction("Démarrer")
    action_demarrer.triggered.connect(functools.partial(demarrer_pendu, main_window))
    action_quitter = menu_partie.addAction("Quitter")
    action_quitter.triggered.connect(main_window.close)

    # Afficher la fenetre et entrer dans la boucle de Qt
    main_window.show()
    app.exec_()

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Exercice 6c:

Décodage basique des informations d'un fichier PNG

Lire la page wikipedia concernant le format PNG, et écrire le programme prenant un fichier en ligne de commande, et affichant le maximum d'informations provenant du fichier

Aide: le module 'struct' permet de décoder des données binaires

2. Installation

3. Premiers pas

Introduction

1. Présentation du langage

Solution de l'exercice 6c

import sys
import struct
import zlib

COLOR_MODES = { 3: "indexed colors",
                2: "truecolor",
                6: "truecolor with alpha",
                0: "greyscale",
                4: "greyscale with alpha" }

COMPRESSION_METHOD = { 0: "deflate" }

def check_signature(f):
    signature = b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"
    assert signature == f.read(len(signature))

def read_chunk(f):
    chunk_length = struct.unpack(">i", f.read(4))[0]
    chunk_type = struct.unpack(">4s", f.read(4))[0]
    chunk_data = f.read(chunk_length)
    chunk_crc = f.read(4)
    return chunk_type, chunk_data

def read_png(filename):
    with open(filename, "rb") as f:
        check_signature(f)
        chunk_type, chunk_data = read_chunk(f)
        assert chunk_type == b"IHDR"
        width, height, depth, color_mode, compression_method, filtering_method, _ = struct.unpack(">iiccccc", chunk_data)
        depth = ord(depth)
        color_mode = ord(color_mode)
        compression_method = ord(compression_method)
        filtering_method = ord(filtering_method)
        assert filtering_method == 0
        print(f"PNG image, {width}x{height}, {depth} bits per channel, color mode: {COLOR_MODES[color_mode]}, compression: {COMPRESSION_METHOD[compression_method]}")
        raw_data = b""
        while True:
            chunk_type, chunk_data = read_chunk(f)
            if chunk_type == b"IEND":
                break
            elif chunk_type == b"IDAT":
                raw_data += chunk_data
        raw_data = zlib.decompress(raw_data)
        # each line of the image in data is in the form: <filter> r g b a
        assert len(raw_data)==width*height*4+height

if __name__ == '__main__':
    read_png(sys.argv[1])

Charmer le Python

Charmer le Python

Générateurs et Coroutines

Gestion de Contexte

Décorateurs

Multiprocessing

4

Fonctionalités avancées

6

POO en Python

Le paradigme objet

Classes, méthodes et objets

Encapsulation

Héritage

Composition

 

 

Evaluation des connaissances

5

Charmer le Python

4. Fonctionalités avancées

Générateurs

5. Evaluation

6. POO en Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Générateurs

Un générateur est une fonction qui se comporte comme un itérateur

Rappel: itérateur = qui renvoie des valeurs dans une boucle for, exemple: list, string, dict, etc.

Charmer le Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Le mot-clef "yield"

 

 

yield a la même fonctionnalité que return, sauf que l'exécution de la fonction ne termine pas: on peut la rappeler pour continuer après yield

L'utilisation du mot-clef yield transforme une fonction en générateur

def f(n):
    i = 0
    while i<n:
      yield i
      i += 1
 
>>> g=f(5)
>>> g
<generator object f at 0x7fe8fe259ee8>
>>> next(g)
0
>>> next(g) 
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

< i est retourné quand le générateur est exécuté; l'exécution suivante reprendra juste après

< appeler f() renvoie un objet "generator"

< la fonction next() déroule le générateur

< StopIteration est levée quand la fonction est terminée

# exemple avec plusieurs yield
def f():
    yield "Début"
    for i in range(3):
        yield i
    yield "Fin"

>>> list(f())
['Début', 0, 1, 2, 'Fin']

Charmer le Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Generator Expression

Cousin de la compréhension de liste, dont l'implémentation utilise un générateur

(expression for i in s if condition)

Syntaxe générale

Traduction avec une boucle "for"

for i in s:

  if condition:

    yield expression

Charmer le Python

Coroutine

Le générateur produit des données ; la coroutine peut aussi consommer des données

def creer_fontaine(contenu): 
    while True:
        x = yield contenu
        if x:
            contenu = x

>>> f = creer_fontaine("soda")
>>> next(f)
soda
>>> next(f)
soda
>>> f.send("lait")
lait
>>> next(f)
lait

< .send() permet d'envoyer  une valeur: le yield en attente retourne la valeur envoyée, et next() est rappelé automatiquement

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Utilisation des générateurs et des coroutines

Lazy Evaluation,

c'est-à-dire évaluation à la dernière minute, pour ne pas devoir préparer toutes les valeurs à l'avance

Processing Pipelines

Machines d'Etats

 

Lien vers l'article

Lien vers l'article

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Gestion de Contexte

Le mot-clef with permet de créer un contexte d'exécution

= un raccourci pour try...finally, plus lisible dans le code, réutilisable, et en un seul bloc

# ouvrir un fichier, le lire, et être
# sûr de le fermer même si une exception survient
try:
    f = open("/tmp/bla")
    ...
finally:
    f.close()
# meme chose avec with
with open("/tmp/bla") as f:
    ...

NB: la fonction doit implémenter le protocole de gestion de contexte... Toutes les fonctions ne gèrent pas les contextes

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Comment écrire un context manager sous forme de fonction

from contextlib import contextmanager
 
@contextmanager
def ma_fonction():
  # tout le code avant yield est exécuté
  # à l'entrée du contexte (au moment du 'with')
  ...
  yield X # la valeur X est récupérable avec 'as'
  ...
  # tout le code après yield est exécuté à la
  # sortie du contexte

< Utilisation du module contextlib de la librairie standard

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 7

Ecrire un context manager qui permet de mesurer le temps (en millisecondes) pris par un bloc de code

 

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Solution de l'exercice 7

from contextlib import contextmanager
import time

@contextmanager
def timeit():
  t0 = time.time()
  yield
  elapsed_time = 1000*(time.time()-t0)
  print("Execution took {} ms".format(elapsed_time))

with timeit():
  print("Je dors...")
  time.sleep(1)


5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

@décorateurs

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Fonction qui permet d'étendre les fonctionalités d'une autre fonction sans la modifier

Qu'est-ce qu'un décorateur ?

Mathématiquement, décorer une fonction correspond à l'opération de composition 'rond' (notée o), telle que pour 2 fonctions f et g:

f o g(x) = f(g(x))

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Ecriture d'un décorateur

# écriture de f o g(x) en Python,
# tel que f o g(x) = f[g(x)]

# 1. fonction décorateur
def f(fonction_à_décorer):

    def fonction_composée(x):
       nom_fonction_décorée = fonction_à_décorer.__name__
       print(f"Bienvenue dans la fonction composée 'f o {nom_fonction_décorée}'")
       print(f"Je renvoie le carré du résultat de {nom_fonction_décorée}")

       res = fonction_à_décorer(x)

       print(f"La fonction {nom_fonction_décorée} a renvoyé {res}")
       print("Je renvoie ce résultat au carré")

       return res**2

    return fonction_composée



# 2. définition
>>> @f
... def g(x):
...   return 2*x
>>>

# 3. Execution

>>> g(3)
Bienvenue dans la fonction composée 'f o g'
Je renvoie le carré du résultat de g
La fonction g a renvoyé 6
Je renvoie ce résultat au carré
36

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exemple d'utilisation: réessayer d'appeler automatiquement une fonction qui produit une exception

def retry_on_exception_decorator(n):
    def decorator(func):
        def wrapped_func(*args, **kwargs):
            i = n
            last_exception = None
            while i > 0:
                try:

                    ret = func(*args, **kwargs)

                except Exception as e:
                    last_exception = e
                    i -= 1
                else:
                    return ret
            raise last_exception
        return wrapped_func
    return decorator

retry = retry_on_exception_decorator(3)

< Closure (fermeture): fonction accompagnée de son environnement lexical (ici, la variable 'n')

>

on ne peut pas modifier les variables d'une closure donc on la réassigne à "i"

import urllib.request

@retry
def get_url(url):
    with urllib.request.urlopen(url) as r:
        if r.getcode() != 200:
            raise RuntimeError(f"Failed to retrieve data from {url}")
        return r.read()

get_url("http://www.google.fr")

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Memoization: LRU (Last Recent Used) cache

Une fonction dotée de "mémoization" conserve les résultats de ses appels successifs, pour retourner immédiatement un résultat si elle est appelée avec les mêmes arguments

from functools import lru_cache

@lru_cache
def do_heavy_job(*args, **kwargs):
    ... some heavy calculation here ...
    return result

>>> do_heavy_job(1,2)
... compute ...
result
>>> do_heavy_job(1,2)
result

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 8: écriture d'un décorateur "check_type" pour vérification du type des arguments d'une fonction

@check_type(str, city=(str,None))
def greeting(name, city=None):
    print(f"Hello, {name}{' from '+city if city else ''} !")

>>> greeting("Alex", "Voiron")
Hello, Alex from Voiron !

>>> greeting("Juan")
Hello, Juan !

>>> greeting("Albert", city=42)
TypeError: Arg. 42 has an invalid type, expected: <class 'str'>

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Solution de l'exercice 8

def check_type(*args, **kwargs):
    def check(x, types):
        try:
            types = tuple(types)
        except TypeError:
            if type(x)!=types:
                raise TypeError(f"Arg. {x} has an invalid type, expected: {str(type)}")
        else:
            if not any(type(x)==t for t in types):
                raise TypeError(f"Arg. {x} has an invalid type, expected one of: {','.join([str(t) for t in types])}")

    def decorator(func):
        def wrapped_func(*func_args, **func_kwargs):
            for arg, arg_types in zip(func_args, args):
                check(arg, arg_types)
            for arg_name, arg in func_kwargs.items():
                arg_types = kwargs[arg_name]
                check(arg, arg_types)
            return func(*func_args, **func_kwargs)
        return wrapped_func

    return decorator

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

6. Projets Python

Multiprocessing

Lire l'excellente présentation de David Beazley sur le sujet ici

(dont sont tirés les schémas de cette partie du cours)

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Notion de concurrence

Programmation concurrente: écriture de programmes pouvant exécuter plusieurs choses à la fois

La concurrence implique le multi-tâche

S'il n'y a qu'un seul CPU, la seule manière d'être multi-tâche est de passer (très) rapidement d'une tâche à une autre

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Parallélisme

Le "vrai" parallélisme intervient quand plusieurs CPU peuvent exécuter des tâches en même temps

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exécution des tâches d'un CPU

Les tâches font:

  • soit des calculs, des opérations sur les variables, du traitement
  • soit des entrées/sorties

< Opération bloquante au niveau du système

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

2 types de programmes

"CPU bound"

"I/O bound" (le plus courant)

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

La concurrence en Python: Threads

Fil d'exécution indépendant

GIL (Global Interpreter Lock)

Mémoire et ressources partagées

Synchronisation,

"race conditions"

Exécution préemptive

opération non-atomiques

"ok" (en faisant bien attention...) pour les tâches "I/O bound"

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

La concurrence en Python: multiprocessing

pas de mémoire partagée

communication via messages (Inter-Process Communication)

pas de limitation du GIL entre les tâches

très utile pour les tâches "CPU bound"

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Utilisation du module multiprocessing

import multiprocessing
import time

def countdown(count):
    while count > 0:
        print("Counting down", count)
        count -= 1
        time.sleep(3)

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=countdown,
                                 args=(10,))
    p1.start()
    p1.join() # attendre la fin du process

Quizz: est-ce que le code présenté dans cet exemple est I/O bound ou CPU bound ?

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Communication entre les process

Un moyen simple et efficace de communiquer entre les process démarrés par multiprocessing est d'utiliser un objet Queue

 


In [1]: from multiprocessing import Process, Queue
   ...: 
   ...: def multiplier(factor, queue):
   ...:     n = queue.get()
   ...:     queue.put(n*factor)
   ...:     

In [2]: q = Queue()

In [3]: p1 = Process(target=multiplier, args=(2, q, ))

In [4]: p1.start()

In [5]: q.put(6)

In [6]: q.get()
Out[6]: 12

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Synchronisation

L'objet multiprocessing.Event est adapté pour permettre de synchroniser les process

from multiprocessing import Process, Queue, Event

started_event = Event()
queue = Queue()

def multiplier(factor, queue, started_event):
    started_event.set()
    n = queue.get()
    queue.put(factor * n)

p1 = Process(target=multiplier, args=(2, queue, started_event))

p1.start()
started_event.wait()
queue.put(6)
print(queue.get()) # affiche 12

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Sérialisation de la communication

Les données qui s'échangent entre les process doivent être sérialisées

 

C'est-à-dire encodées pour pouvoir être transférées

 

L'objet Queue de multiprocessing le gère de façon transparente à l'aide du module pickle

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

multiprocessing.Pool

L'objet Pool de multiprocessing offre une méthode pratique pour paralléliser l'éxécution de code

 

Pool.map s'utilise de la même manière que la fonction "map" built-in

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

6. Projets Python

Exercice 9: convertir une image en niveaux de gris

Utiliser le module multiprocessing pour convertir une image couleur de Lena en niveaux de gris

 

Decouper l'image en parties et faire la moyenne des valeurs R,G,B de chaque pixel dans des process Python differents (utilisation de multiprocessing.Pool)

 

Ecrire la nouvelle image en niveaux de gris dans le fichier choisi par l'utilisateur au format .png

 

Utiliser le package pillow (PIL), bien penser à itertools, functools, collections qui peuvent être utiles

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

6. Projets Python

Comparer le temps d'exécution avec un seul thread (= sans multiprocessing)

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

6. Projets Python

Recommencer avec une image beaucoup plus grande

(liens ici)

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

6. Projets Python

Une solution de l'exercice 9: ici

 

ou bien, plus moderne:

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

from PIL import Image
import requests
import io
import multiprocessing
import itertools
import more_itertools
import contextlib
import time

@contextlib.contextmanager
def timeit():
    t0 = time.perf_counter()
    yield
    print(f"Function took {1000*(time.perf_counter()-t0)} ms.")

imgdata = requests.get("https://i.stack.imgur.com/3T6Gc.jpg").content

imgdata_as_a_file = io.BytesIO(imgdata)

im = Image.open(imgdata_as_a_file)

def convert_to_greyscale(data):
    return [sum(rgb)//3 for rgb in data]

with timeit():
    pixels_greyscale = convert_to_greyscale(im.getdata())

#im_greyscale = Image.new("L", im.size)
#im_greyscale.putdata(pixels_greyscale)

pixels = im.width * im.height
ncpus = multiprocessing.cpu_count()
chunk_size = pixels//ncpus

with timeit():
    with multiprocessing.Pool(ncpus) as pool:
        sub_data = pool.map(convert_to_greyscale, more_itertools.chunked(im.getdata(), chunk_size))

    pixels_greyscale = itertools.chain(*sub_data)

im_greyscale = Image.new("L", im.size)
im_greyscale.putdata(list(pixels_greyscale))
im_greyscale.show()

6. Projets Python

Suite de l'exercice 9

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

A la suite de la conversion en niveaux de gris, calculer l'histogramme de l'image

(c'est-à-dire: compter combien il y a de pixels de chaque niveau de gris)

 

Sauvegarder l'histogramme à l'aide du package matplotlib, sous forme de fichier .png

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Corrigé du test ici

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Programmation Orientée Object en Python

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Le paradigme objet

Modéliser les données et les opérations sur les données d'un problème avec des représentants abstraits (des objets dans du code)

Les objets sont issus de classes

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Qu'est-ce qu'une classe ?

classe

= le "patron" (au sens manufacturier) des objets

objet

= instance de la classe

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

4 concepts clefs

Abstraction

Eliminer le superflu, amplifier l'essentiel

Cacher ce qui n'est pas nécessaire de connaître

Modélisation de la similarité

Même fonction, différents comportements

Héritage

Polymorphisme

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Représentation schématique d'une classe

MachineACafé
propriétés
+ finesse_mouture
+ temperature
méthodes privées
- _chauffer_eau()
- _moudre_le_grain(quantité)
- _faire_couler(quantité_eau)
méthodes publiques
+ faire_café(quantité_café, quantité_eau)

< utilisées en interne, non disponibles pour l'utilisateur de l'objet final (en théorie)

< API utilisateur

< équivalent de "getter" et "setter" en Java ; peuvent être publiques ou privées, selon les mêmes conventions que les méthodes

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Enough talk, show me the code !


class MachineACafé:
    def __init__(self):
        self._temperature = 50
        self._finesse_mouture = "moyen"

    def _chauffer_eau(self):
        pass

    def _moudre_grains(self, quantité):
        pass

    def _faire_couler(self, quantité_eau):
        pass

    def _signaler_café_prêt(self):
        print("Veuillez prendre votre café.")

    def faire_café(self, quantité_café, quantité_eau):
        self._chauffer_eau()
        self._moudre_grains(quantité_café)
        self._faire_couler(quantité_eau)
        self._signaler_café_prêt()

< méthode spéciale : __init__ est le "constructeur" de la classe

< toutes les méthodes reçoivent un premier argument spécial: l'instance de l'objet sur lequel la méthode est appelé (traditionnellement appelé "self" -ou "this" en Javascript)

>>> delonghi_pro = MachineACafé()
>>> type(delonghi_pro)
<class '__main__.MachineACafé'>
>>> dir(delonghi_pro)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__',
'_chauffer_eau', '_faire_couler', '_finesse_mouture', '_moudre_grains',
'_signaler_café_prêt', '_temperature', 'faire_café']
>>> delonghi_pro.faire_café(10,80)
Veuillez prendre votre café.

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 10

  • écrire une classe pour représenter une porte (méthodes ouvrir(), fermer(), propriété état -ouvert ou fermé- etc.)

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Encapsulation:

Le décorateur @property

Similaire au principe du "getter/setter" populaire en Java, mais à la sauce Python

 

Permet d'emballer (encapsuler) des détails d'implémentation dans une API plus simple

 

Les propriétés peuvent être en lecture seule (read-only) ou en lecture/écriture

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exemple de @property

class Animal:
    def __init__(self, type, couleur):
        self._type = type
        self._couleur = couleur

    @property
    def type(self):
        return self._type

    @property
    def couleur(self):
        return self._couleur
>>> titi=Animal("oiseau", "jaune")
>>> titi.type
'oiseau'
>>> titi.type="chat"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> 

< par défaut une property est read-only

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exemple de @property (2)

class Animal:
    def __init__(self, type, couleur):
        self._type = type
        self._couleur = couleur
        self._état = None

    @property
    def état(self):
        if not self._état:
            return "inconnu"
        else:
            return self._état

    @état.setter
    def état(self, nouvel_état):
        états_autorisés = ("endormi", "éveillé", "agressif", "en rut")
        if not nouvel_état in états_autorisés:
            raise ValueError(f"Etat invalide, choisir parmi: {', '.join(états_autorisés)}")
        self._état = nouvel_état

>>> a=Animal("oiseau", "jaune")
>>> a.état
'inconnu'
>>> a.état="joyeux"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/bla.py", line 18, in état
    raise ValueError(f"Etat invalide, choisir parmi: {', '.join(états_autorisés)}")
ValueError: Etat invalide, choisir parmi: endormi, éveillé, agressif, en rut
>>> a.état="en rut"
>>> 

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 11

Ecrire une classe pour représenter un cercle avec des propriétés rayon, diamètre, et des méthodes aire(), périmètre()

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Solution de l'exercice 11

import math

class Cercle:
    def __init__(self, rayon):
        self._rayon = rayon

    @property
    def rayon(self):
        return self._rayon

    @rayon.setter
    def rayon(self, r):
        self._rayon = r

    @property
    def diametre(self):
        return 2*self.rayon

    @diametre.setter
    def diametre(self, d): 
        self.rayon = d/2.

    def calculer_aire(self):
        return math.pi*self.rayon**2

    def calculer_perimetre(self):
        return 2*math.pi*self.rayon

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 11b: compte bancaire

  1. Créer une classe Python nommée CompteBancaire qui représente un compte bancaire, ayant pour propriétés : numeroCompte, nom (nom du propriétaire du compte du type chaine), solde.
  2. Créer un constructeur ayant comme paramètres : numeroCompte, nom, solde.
  3. Créer une méthode Versement() qui gère les versements.
  4. Créer une méthode Retrait() qui gère les retraits
    • Empêcher d'être à découvert
  5. Utiliser __repr__ pour faire un affichage des informations du compte

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Solution de l'exercice 11b

class CompteBancaire: 
    def __init__(self, numeroCompte, nom, solde): 
        self.__numeroCompte = numeroCompte 
        self.__nom = nom 
        self.__solde = solde 
 
    @property 
    def numeroCompte(self): 
        return self.__numeroCompte 
 
    @property 
    def nom(self): 
        return self.__nom 
 
    @property 
    def solde(self): 
        return self.__solde 
 
    def versement(self, somme): 
        self.__solde += somme 
 
    def retrait(self, somme): 
        if self.__solde < somme: 
            raise ValueError("Désolé la banque ne fait pas crédit") 
        self.__solde -= somme 
 
    def __repr__(self): 
        print(f"Compte numero {self.numeroCompte}") 
        print(f"Client: {self.nom}") 
        print(f"Solde: {self.solde} euros")

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 11c: compte bancaire II

6. Créer une méthode "ajouterTitulaire" pour créer un compte joint (2 personnes, ou plus)

  • penser à modifier l'affichage

7. créer une propriété découvertAutorisé qui donne le droit d'être à découvert, jusqu'à un montant maximal

8. ajouter une méthode pour appliquer des frais lors d'un retrait en cas de découvert (5% du montant total du découvert)

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Solution exercice 11c

import random

class CompteBancaire:
    def __init__(self, nom, solde):
        self.__numeroCompte = 100000+random.randint(0,100000)
        self.__nom = nom
        self.__solde = solde
        self.__cotitulaires = []
        self.__decouvertAutorise = 0

    @property
    def numeroCompte(self):
        return self.__numeroCompte

    @property
    def nom(self):
        return self.__nom

    @property
    def solde(self):
        return self.__solde

    @property
    def titulaires(self):
        return [self.nom] + self.__cotitulaires

    @property
    def decouvertAutorise(self):
        return self.__decouvertAutorise

    @decouvertAutorise.setter
    def decouvertAutorise(self, somme):
        self.__decouvertAutorise = somme

    def versement(self, somme):
        self.__solde += somme

    def retrait(self, somme):
        if self.__solde < somme:
            if abs(self.__solde - somme) > self.decouvertAutorise:
                raise ValueError("Désolé la banque ne fait pas crédit")
        self.__solde -= somme
        self.appliquerFrais()

    def appliquerFrais(self):
        if self.__solde < 0:
            self.__solde -= 0.05*abs(self.__solde) 

    def __repr__(self):
        s = f"Compte numero {self.numeroCompte}\n"
        s += f"{'Titulaire' if not self.__cotitulaires else 'Titulaires'}: {', '.join(self.titulaires)}\n"
        s += f"Solde: {self.solde} euros\n"
        return s
        
    def ajouterTitulaire(self, nom):
        self.__cotitulaires.append(nom)

    
if __name__ == "__main__":
    cpte = CompteBancaire("Martin", 5000)
    assert cpte.numeroCompte
    assert cpte.solde == 5000
    assert cpte.nom == "Martin"
    cpte.ajouterTitulaire("Olivia")
    assert cpte.titulaires == ["Martin", "Olivia"]
    cpte.versement(3000)
    assert cpte.solde == 8000
    cpte.retrait(4000)
    assert cpte.solde == 4000
    assert cpte.decouvertAutorise == 0
    try:
        cpte.retrait(5000)
    except ValueError:
        assert True
    else:
        assert False
    cpte.decouvertAutorise = 1000
    assert cpte.decouvertAutorise == 1000
    cpte.retrait(4500)
    assert cpte.solde == -500-(0.05*500)
    cpte.retrait(300)
    assert cpte.solde == -825-(0.05*825)
    cpte.versement(3000)
    assert cpte.solde == 2133.75

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Méthodes spéciales

Il existe de nombreuses méthodes spéciales (cf. la doc Python...)

 

__init__() est la plus connue : constructeur de la classe

__str__() est bien utile aussi

class Animal:
    ...

    def __str__(self):
        return f"Je suis un {self.type} {self.état}."


>>> gros_minet=Animal("chat", "noir")
>>> gros_minet.état="agressif"
>>> print(gros_minet)
'Je suis un chat agressif.'
>>> 

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Héritage

Permettre de créer une nouvelle classe en réutilisant une classe existante

 

Uniquement valable lorsqu'il y a une relation "EST UN" ("IS A") entre 2 objets

 

La nouvelle classe dispose de toutes les propriétés et des méthodes de la classe de base, et peut les modifier ou les étendre

 

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Héritage (2)

Ne pas comprendre la relation "IS A" au sens le plus strict... C'est vrai dans 80% des cas seulement

 

Par exemple: le cas du rectangle et du carré

 

Un carré est un rectangle MAIS les comportements des deux objets sont trop différents, comme avec la largeur et la longueur. Voir le LSP (lien ici)

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exemple d'Héritage

class AnimalDomestique(Animal):
    def __init__(self, nom, type, couleur):
        super().__init__(type, couleur)
        self._nom = nom
    
    @property
    def nom(self):
        return self._nom

    def __str__(self):
        base_str = super().__str__()
        return base_str+f" Je m'appelle {self.nom}."

< super() permet d'appeler la classe de base (ici, le constructeur d'Animal)

>>> coco=AnimalDomestique("Coco", "oiseau", "vert")
>>> coco.état="endormi"
>>> print(coco)
"Je suis un oiseau endormi. Je m'appelle Coco."
>>> 

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 12: écrire la classe Chien et la classe Chat

Le chien EST UN animal domestique

 

Le chat aussi

 

Ajouter une méthode "aboyer" au chien

Ajouter une méthode "miauler" au chat

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Composition

La composition consiste à ajouter des méthodes ou des propriétés à une classe en déléguant à d'autres objets

class PuceElectronique:
    def __init__(self, id):
        self._id = id
        self._nom_proprio = None
        self._adresse_proprio = None

    @property
    def id(self):
        return self._id

    @property
    def nom_proprio(self):
        return self._nom_proprio

    @property
    def adresse_proprio(self):
        return self._adresse_proprio

    def définir_propriétaire(self, nom, adresse):
        self._nom = nom
        self._adresse = adresse

class AnimalDomestique:
     def __init__(...):
         self._puce = None

     def mettre_puce(self, puce):
         self._puce = puce

     @property
     def propriétaire(self):
         if not self._puce:
             return ""
         return self._puce.nom_proprio

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Composition vs Héritage

Dans la programmation de tous les jours, la composition est beaucoup plus utile que l'héritage

 

Soyez prudent avec l'héritage, en tant que développeur débutant

(pareil que les threads !)

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Exercice 13: porte avec serrure

Réutiliser la classe Porte de l'exercice précédent, pour créer une classe PorteAvecSerrure

 

Les serrures peuvent être de 2 types:

  • à clef
  • à digicode

 

Utiliser la composition

6. Projets Python

5. Evaluation

6. POO en Python

4. Fonctionalités avancées

Charmer le Python

Pour des explications peu conventionnelles et un peu datées mais néanmoins très utiles c'est par ici

Pour aller plus loin...

Python en Prod

Python en Prod

6

Projets Python

Debugging

Ecriture de tests

Fichier setup.py