Licence CC BY-NC-ND, Thierry Parmentelat & Arnaud Legout

from IPython.display import HTML
HTML(filename="_static/style.html")

la librairie standard#

le tutorial Python sur ce sujet occupe à lui tout seul deux chapitres: chapitre 10 et chapitre 11
le spectre est très très complet, je fais ici un tri totalement arbitraire…

logging#

dans du code de production on ne fait jamais print() ! on utilise à la place logging, car de cette façon:

  • le code a seulement à choisir un niveau de message parmi exception, error, warning, info, debug

  • on pourra choisir plus tard (i.e. par l’équipe Ops) :

    • si on veut les messages (avec quel niveau de gravité, et depuis quels modules)

    • doivent aller les messages (/var/log, syslog, stdout, …)

# voici comment configurer logging pour avoir un comportement 'à la' print

import logging
logging.basicConfig(level=logging.INFO)
# au lieu de faire 

print(f"Bonjour le monde") 
Bonjour le monde
# on fera plutôt

logging.info("Bonjour le monde")
INFO:root:Bonjour le monde
# ou encore 

logging.error("OOPS")
ERROR:root:OOPS

sys et os#

sont utilisés très fréquemment, surtout dans du code ancien, pour

  • import sys

    • gestions de variables utilisées par l’interpréteur

    • e.g. sys.path, et plein plein d’autres dans ce module un peu fourre-tout

  • import os

    • accès cross-platform au système d’exploitation

      os n’est plus vraiment utile

      notez toutefois que ses usages principaux ont été remplacés:

      • par pathlib pour les fichiers (voir slide suivant)

      • par subprocess pour lancer des sous-processes

from pathlib import Path#

( import os.path)

historiquement on gérait les noms de fichiers sur disque avec le sous-module os.path
mais depuis la 3.4 une alternative orientée objet est disponible!

n’utilisez plus os.path !

il faut utiliser pathlib.Path pour du nouveau code ! on peut tout faire avec:

  • chercher (glob) tous les fichiers en *.truc

  • calculer les noms de fichier: concaténer, découper en morceaux, trouver le nom canonique

  • ouvrir les fichier

  • accéder aux métadata (taille, date, ..)

  • etc…

from pathlib import Path

for path in Path(".").glob("samples/*.py"):
    # le nom canonique
    print(10*'=', path.absolute())
    # le dernier morceau dans le nom
    print(path.name, end=' ')
    # la taille
    print(path.stat().st_size, 'bytes')
    # touver le nom absolu
    # ouvrir le fichier en lecture
    with path.open():
        pass
========== /home/docs/checkouts/readthedocs.org/user_builds/ue12-p24-python/checkouts/p24/notebooks/samples/typehints.py
typehints.py 173 bytes
========== /home/docs/checkouts/readthedocs.org/user_builds/ue12-p24-python/checkouts/p24/notebooks/samples/fact_argparse.py
fact_argparse.py 1286 bytes
========== /home/docs/checkouts/readthedocs.org/user_builds/ue12-p24-python/checkouts/p24/notebooks/samples/closures.py
closures.py 254 bytes
========== /home/docs/checkouts/readthedocs.org/user_builds/ue12-p24-python/checkouts/p24/notebooks/samples/fib.py
fib.py 307 bytes
========== /home/docs/checkouts/readthedocs.org/user_builds/ue12-p24-python/checkouts/p24/notebooks/samples/fact_sysargv2.py
fact_sysargv2.py 532 bytes
========== /home/docs/checkouts/readthedocs.org/user_builds/ue12-p24-python/checkouts/p24/notebooks/samples/fact_sysargv.py
fact_sysargv.py 739 bytes

orienté objet

avec pathlib, les calculs de chemin se font directement à base de l’opérateur /

# c'est très facile de se promener dans l'arbre des fichiers

répertoire = Path(".")
fichier = répertoire / "samples" / "fib.py"

with fichier.open() as feed:
    for lineno, line in enumerate(feed, 1):
        print(f"{lineno}:{line}", end="")  
1:# un module définit souvent des fonctions et classes
2:
3:def fib(n):
4:    a, b = 0, 1
5:    while b < n:
6:        print(b, end=' ')
7:        a, b = b, a + b
8:
9:
10:# le code qui suit sera exécuté seulement si on le lance
11:# depuis la ligne de commande, et pas pendant un import
12:
13:if __name__ == '__main__':
14:    fib(40)

datetime, math et random#

datetime#

  • gestion des dates et des heures

math#

  • fonctions mathématiques, constantes, …

random#

  • générations de nombres et séquences aléatoires, mélange aléatoire de séquences

formats de fichier#

json#

  • sérialisation d’objets python, standard du web

  • envoi et réception depuis toutes sources compatibles json

csv#

  • ouverture fichier csv, compatible Excel et tableurs

pickle#

  • sérialisation d’objets python, uniquement compatible avec python

  • sauvegarde et la chargement du disque dur

le module collections#

une extension des objets built-in list, tuple, dict
la doc est ici, voici une petite sélection

collections.Counter()#

à partir d’un itérable, construit un dictionnaire qui contient

  • comme clefs les éléments uniques

  • et comme valeurs le nombre de fois que l’élément apparaît

from collections import Counter

cnt = Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
isinstance(cnt, dict)
True
# par exemple pour compter les mots dans un texte

import re
words = re.findall(r'\w+', open('../data/hamlet.txt').read().lower())

Counter(words).most_common(10)
[('the', 1108),
 ('and', 920),
 ('to', 762),
 ('of', 699),
 ('you', 593),
 ('i', 587),
 ('a', 557),
 ('my', 506),
 ('it', 443),
 ('in', 426)]

collections.defaultdict()#

from collections import defaultdict

# on va fabriquer un dict    word -> liste d'indices où il apparait
s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]

# pour cela on indique que les valeurs sont des listes
d = defaultdict(list)

# si on écrit une clé qui n'est pas encore présente
# d[k] vaut alors list()
# c'est-à-dire une liste vide est crée automatiquement
for k, v in s:
    d[k].append(v)

sorted(d.items())
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

itertools - combinatoire#

  • implémente sous forme efficace (itérateurs)

  • des combinatoires classiques

  • et autres outils utiles pour écrire des boucles concises

  • la doc

  • déjà abordé dans la partie 3.3 sur les itérateurs

fournit les combinatoires communes

itertools - produit cartésien#

from itertools import product
A = ['a', 'b', 'c']
B = [ 1, 2]

for x, y in product(A, B):
    print(x, y)
a 1
a 2
b 1
b 2
c 1
c 2

itertools - permutations#

from itertools import permutations
C = ['a', 'b', 'c', 'd']

for tuple in permutations(C):
    print(tuple)
('a', 'b', 'c', 'd')
('a', 'b', 'd', 'c')
('a', 'c', 'b', 'd')
('a', 'c', 'd', 'b')
('a', 'd', 'b', 'c')
('a', 'd', 'c', 'b')
('b', 'a', 'c', 'd')
('b', 'a', 'd', 'c')
('b', 'c', 'a', 'd')
('b', 'c', 'd', 'a')
('b', 'd', 'a', 'c')
('b', 'd', 'c', 'a')
('c', 'a', 'b', 'd')
('c', 'a', 'd', 'b')
('c', 'b', 'a', 'd')
('c', 'b', 'd', 'a')
('c', 'd', 'a', 'b')
('c', 'd', 'b', 'a')
('d', 'a', 'b', 'c')
('d', 'a', 'c', 'b')
('d', 'b', 'a', 'c')
('d', 'b', 'c', 'a')
('d', 'c', 'a', 'b')
('d', 'c', 'b', 'a')

itertools - combinaisons#

from itertools import combinations
miniloto = list(range(5))

for a, b in combinations(miniloto, 2):
    print(a, b)
0 1
0 2
0 3
0 4
1 2
1 3
1 4
2 3
2 4
3 4
# les arrangements ne sont pas disponibles tel-quel
# mais une possibilité est de générer toutes les permutations
# de chaque tirage dans les combinaisons

def arrangements(collection, n):
    for tuple_ in combinations(collection, n):
        yield from permutations(tuple_)

for t in arrangements(miniloto, 2):
    print(t)
(0, 1)
(1, 0)
(0, 2)
(2, 0)
(0, 3)
(3, 0)
(0, 4)
(4, 0)
(1, 2)
(2, 1)
(1, 3)
(3, 1)
(1, 4)
(4, 1)
(2, 3)
(3, 2)
(2, 4)
(4, 2)
(3, 4)
(4, 3)

module itertools - divers#

  • parfois sans fin

    • count(10) --> 10 11 12 13 14 ...

    • cycle('abcd') --> a b c d a b c d ...

  • ou pas

    • islice('abcdefg', 2, none) --> c d e f g

    • repeat(10, 3) --> 10 10 10

module itertools - suite#

  • chainer plusieurs itérations

    • chain('ABC', 'DEF') --> A B C D E F

    • chain.from_iterable(['ABC', 'DEF']) --> A B C D E F

  • avec un peu de logique

    • takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4

    • dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1

    • compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F

    • filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8

from itertools import filterfalse
%timeit -n 100 for x in filterfalse(lambda x:x%2, range(10000)): pass
959 μs ± 4.23 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit -n 100 for x in (y for y in range(10000) if not (y % 2)): pass
519 μs ± 3.46 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)

operator#

  • en python tout est un objet, on peut donc tout passer à une fonction, mais comment passer un opérateur comme +, in, ou >

  • le module operator contient la version fonctionnelle d’un grand nombre d’opérateurs python

import random
l = [('a', random.randint(1, 1000)) for i in range(100)]
l.sort(key=lambda x: x[1])
l[-7:]
[('a', 975),
 ('a', 984),
 ('a', 994),
 ('a', 995),
 ('a', 996),
 ('a', 997),
 ('a', 1000)]
l = [('a', random.randint(1, 1000)) for i in range(100)]
import operator
l.sort(key=operator.itemgetter(1))
l[-7:]
[('a', 905),
 ('a', 911),
 ('a', 919),
 ('a', 968),
 ('a', 970),
 ('a', 971),
 ('a', 988)]

et plein d’autres

à nouveau cette présentation est loin d’être exhaustive, n’hésitez pas à aller farfouiller par vous-même en fonction de vos besoins…