1. Langage Python#

Création début années 90 par Guido van Rossum (Pays-Bas)

  • Version 2.0 en 2000

  • Version 3.0 en 2008 (non retro-compatible)

Caractéristiques:

  • langage haut-niveau, interprété

  • multi-paradigme: programmation impérative, fonctionnelle et orientée objet

  • typage dynamique (type déterminé à l’execution)

  • typage fort (pas de conversion implicite)

  • gestion automatique de la mémoire (garbage collector)

  • syntaxe lisible et épuré

Langage de plus en plus utilisé: beaucoup de contributions et de librairies (modules) disponibles pour le calcul scientifique (alternative à MATLAB/Scilab), l’analyse de données (alternative à R) et le machine learning (sklearn) ou le deep learning (pytorch, tensorflow).

from platform import python_version
print("Version de python utilisée dans ce notebook:", python_version())
Version de python utilisée dans ce notebook: 3.11.11

1.1. Modules, mots-clés#

1.1.1. Import d’un module#

import math                  # import du module math
math.sqrt(25)
5.0
from math import sqrt        # import d'une fonction
sqrt(25)                     # plus besoin du nom du module
5.0
from math import cos, floor  # import de plusieurs fonctions
from csv import *      # import de toutes les fonctions d'un module (déconseillé)
import datetime as dt  # définition d'un alias
print(dir(math))             # liste des fonctions du module math
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'cbrt', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'exp2', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'lcm', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'nextafter', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc', 'ulp']

1.1.2. Mots-clés réservés#

import keyword
print(keyword.kwlist)       # liste des mots-clés du langage
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

Pour connaître les fonctions disponibles on consulte la page de la documentation officielle. Voici une façon d’afficher une page web à l’intérieur d’un notebook:

from IPython.display import IFrame
doc = IFrame(src='https://docs.python.org/3/library/functions.html#built-in-funcs',
             width=700,height=500)
doc
help(round)
Help on built-in function round in module builtins:

round(number, ndigits=None)
    Round a number to a given precision in decimal digits.
    
    The return value is an integer if ndigits is omitted or None.  Otherwise
    the return value has the same type as the number.  ndigits may be negative.
round?

1.1.3. Quelques librairies python#

Domaine

Nom du module

N-dimensional array package

NumPy

Scientific Computation

SciPy

Plotting

Matplotlib, seaborn

Data Analysis

pandas

Symbolic Computation

Sympy

Networking

networkx

Machine Learning

scikit-learn

Deep Learning

tensorflow, pytorch

Image Processing

scikit-image

Natural Language Processing

nltk

Cryptography

pyOpenSSL

Game Development

PyGame

Graphic User Interface

pyQT

Database

SQLAlchemy

HTML and XML parsing

BeautifulSoup

Il existe aussi des modules pour améliorer les performances du code python en utilisant de la compilation à la volée (Just In Time compilation ou JIT):

  • Numba compilation CPU/GPU

  • JAX différentiation automatique et compilation CPU/GPU

1.2. Généralités: types, opérations, tests et boucles#

Il existe trois types numériques distincts:

  • int pour les entiers (précision illimitée),

  • float pour les réels (nombres flottants, précision dépend de l’architecture),

  • complex pour les nombres complexes (constante imaginaire pure: j).

Il y a un type bool pour les expressions booléenes dont les constantes sont True et False.

La documentation fournit toutes les informations détaillées.

1.2.1. Manipulation de types#

x = 2.0
type(x)
float
x = '2'     # x change de type, le x du contexte précédent est écrasé
type(x)
str
isinstance(x, int)      # est-ce que x est un entier ? 
False
x = int(x)  # conversion de la string en int (erreur à l'execution si impossible)
isinstance(x, (int, float))  # est-ce que x est un entier ou un réel ?
True
x
2
print("La constante True est de type: ", type(True))
print("La constante None est de type: ", type(None))
print("Le nombre 2+3j est de type:", type(2+3j))
La constante True est de type:  <class 'bool'>
La constante None est de type:  <class 'NoneType'>
Le nombre 2+3j est de type: <class 'complex'>

1.2.2. Types composés: structure de données#

Il y a quelques types composés qu’il faut maitriser. On détaille l’usage plus loin dans ce notebook. On peut regrouper les structures ordonnées:

  • une collection hétérogène modifiable list []

  • une collection non modifiable tuple ()

  • une chaine de caractère str "" ou ''

type([1, 'a', 0.2])
list
type((1, 'a', 0.2))
tuple
type("abc")
str

Et les structures non ordonnées:

  • structure associative (table de hashage) ou dictionnaire dict {:}

  • collection non ordonnée: set {} ou frozenset

from math import exp, pi 
cnsts = {'e': exp(1), 'pi': pi}
print(cnsts)
type(cnsts)
{'e': 2.718281828459045, 'pi': 3.141592653589793}
dict
ens = {1, 'a', 0.2, "abc"}
print(ens)
type(ens)
{0.2, 1, 'a', 'abc'}
set

D’autres types composés sont disponibles dans le module collections.

1.2.3. Opérations arithmétiques#

20 + 3          # 23   (type int)
20 + 3.         # 23.0 (type float)

20 * 3          # 60
20 ** 3         # 8000 (puissance)

20 / 3          # 6.666666666666667 
20 // 3         # 6 (division entière) 

20 % 3          # 2 (modulo) 
(2+3j) + (4-7j) # 6-4j  
(9+5j).real     # 9.0 
(9+5j).imag     # 5.0 
abs(3+4j)       # 5.0 (module)
5.0
'a' + 'b'       # concaténation
'ab'
# si on execute cette cellule on a une erreur
#'a' + 2         # fortement typé, différent du javascript

1.2.4. Remarques sur le type int#

En Python les entiers sont codés avec un type particulier dont le nombre de bits en mémoire est variable: cela permet de manipuler des entiers aussi grands que l’on veut.

Par exemple on peut afficher les nombres suivants: \(10!\), \(100!\), \(1000!\)

from math import factorial
print("10! = ", factorial(10))
print("100! = ", factorial(100))
print("1000! = ", factorial(1000))
10! =  3628800
100! =  93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
1000! =  402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Pour information, on peut connaitre la taille en mémoire d’une variable python en utilisant la fonction getsizeof du module sys.

from sys import getsizeof
help(getsizeof)
print(getsizeof(factorial(10)))
print(getsizeof(factorial(100)))
print(getsizeof(factorial(1000)))
Help on built-in function getsizeof in module sys:

getsizeof(...)
    getsizeof(object [, default]) -> int
    
    Return the size of object in bytes.

28
96
1164

Si on compare après conversion d’un int en float (type pour les réels similaire à double en C/C++) de taille fixe.

print(getsizeof(float(factorial(10))))
print(getsizeof(float(factorial(100))))
# il y a une erreur si on execute l'instruction suivante:
# print(getsizeof(float(factorial(1000))))
24
24

Remarque: la taille maximale d’un float est de ? cf. wikipedia.

1.2.5. Opérations booléennes#

x = 5
x >= 3
True
print(x != 3, x == 3)
True False
x > 3 and 6 > 3     # 'et' booléen
True
x > 3 or 5 < 3     # 'ou' booléen
True
not x > 3
False

Priorité (ordre d’évaluation): not, and puis or

1.2.6. Tests if/elif/else#

if x > 0:
    print('positive') # attention: indentation permet de définir un bloc d'instructions
    print('coucou')
else:                 # else facultatif
    print('zero or negative')
positive
coucou
if x > 0:
    print('positive')
elif x == 0:                   # elif pour enchainer les conditions
    print('zero')
else:
    print('negative')
positive
if x > 0: print('positive')    # possible en une ligne
positive

Possibilité d’écrire une expression conditionnelle

'positive' if x > 0 else 'zero or negative'    # expression conditionnelle: en C++ (x > 0) ? 'positive' : 'zero or negative' 
'positive'

1.2.7. Itérations, boucles#

1.2.7.1. Boucle while#

N = 0
x = x0 = 1024
while x > 0: 
    x //= 2
    N += 1
print(f"Approximation de log_2({x0}): {N-1}")
Approximation de log_2(1024): 10

1.2.7.2. Itérable et range#

range(10)         # définit la plage itérable {0,1,2,..,9} (ouvert à droite)
range?
for i in range(0, 101, 10):
    print(i)
0
10
20
30
40
50
60
70
80
90
100

1.2.7.3. Boucle for#

jours = ['lundi', 'mardi', 'mercredi']   # une liste
for i in range(len(jours)):              # à éviter
    print(jours[i])
lundi
mardi
mercredi
# erreur si on execute cette cellule 
#jours = {'lundi', 'mardi', 'mercredi'}   # un ensemble
#for i in range(len(jours)):              
#    print(jours[i])                      # ne fonctionne pas
for day in jours:          # fonctionne dès que jours est itérable
    print(day)
lundi
mardi
mercredi

Utilisation d’ enumerate

for index, day in enumerate(jours):
    print(index, day)
0 lundi
1 mardi
2 mercredi

Utilisation de zip

planetes = { 'mars', 'mercure', 'terre', 'venus' }  # ensemble non ordonné! 
print(planetes)
{'mars', 'mercure', 'terre', 'venus'}
for jour, planete in zip(jours, planetes): # parcourt conjoint des itérables jours et Ps
    print(jour, planete)
lundi mars
mardi mercure
mercredi terre

Boucle for/else

for day in jours:          # fonctionne dès que jours est itérable
    if day == 'dimanche':
        print(day)
        break
else:                      # attention à l'indentation
    print('pas de dimanche!')
pas de dimanche!

1.3. Fonctions#

Attention une fonction peut dépendre et interagir avec un contexte global: ce n’est pas une fonction au sens mathématique.

Une fonction qui ne dépend que de ses arguments est dite fonction pure ou sans effet de bord: écriture recommandée.

Syntaxe:

def nom_fonction(arguments):
      instructions

Pour renvoyer une valeur on utilise le mot-clé return.

1.3.1. Arguments#

  • positional argument: un argument qui n’est pas suivi par un signe = (n’a pas de valeur par défaut)

  • keyword argument: argument suivi par un signe = et une expression qui donne une valeur par défaut

On indique d’abord les positional arguments puis les keyword arguments.

def g(a, b, t=0.5):
    z = a + t * (b - a)       # z est une variable locale
    return z
print( g(1, 6) )             # t prend la valeur par defaut, a vaut 1 et b vaut 6
print( g(1, 6, 0.2) )        # a et b sont déterminés par la position
print( g(t=0.2, b=6, a=1) )  # arguments déterminés par leurs noms
print( g(b=6, a=1) )         
print( g(1, t=0.2, b=6) )   # a vaut 1 car en première position
print( g(1, b=6) ) 
3.5
2.0
2.0
3.5
2.0
3.5

1.3.2. Valeurs de retour#

epsilon = 0.1                         # variable globale
def move(x, y):
    return x + epsilon, y * epsilon   # fonction retourne un tuple
move(0.1, 10)
(0.2, 1.0)
epsilon = 0.5
move(0.1, 10)             # comportement dépend du contexte: à éviter en général
(0.6, 5.0)
a, b = move(0.1, 10)      # tuple unpacking
print("a =", a)
print("b =", b)
a = 0.6
b = 5.0

1.3.3. Fonctions anonymes lambda#

squared = lambda x: x**2   # fonction anonyme stockée dans la variable squared
squared(4)
16

Exemple d’utilisation: trier une liste de mot dont l’ordre est donné par la dernière lettre

planetes
{'mars', 'mercure', 'terre', 'venus'}
def last_letter(word):
    return word[-1]
sorted(planetes, key=last_letter)           # version avec la fonction last_letter
['mercure', 'terre', 'mars', 'venus']
?sorted
sorted(planetes, key=lambda word: word[-1]) # version avec une lambda fonction
['mercure', 'terre', 'mars', 'venus']

1.3.4. Modification des arguments#

Les arguments sont passés par adresse et peuvent dont être modifiés par une fonction (s’ils sont de type modifiable: pas le cas des tuples, frozenset…)

def repeat(x):
    x *= 2
    return x

a = [1, 2]  # liste modifiable

print("Argument avant l'appel:\t", a)
print("Résultat de l'appel:\t", repeat(a))
print("Argument après l'appel:\t", a)
Argument avant l'appel:	 [1, 2]
Résultat de l'appel:	 [1, 2, 1, 2]
Argument après l'appel:	 [1, 2, 1, 2]
a = (1, 2)  # tuple non modifiable

print("Argument avant l'appel:\t", a)
print("Résultat de l'appel:\t", repeat(a))
print("Argument après l'appel:\t", a)
Argument avant l'appel:	 (1, 2)
Résultat de l'appel:	 (1, 2, 1, 2)
Argument après l'appel:	 (1, 2)

1.4. Détails des structures composées:#

1.4.1. Listes []#

  • collection hétérogène: composée d’objets de types quelconques

  • collection ordonnée, itérable, indexée à partir de 0
    (indices négatifs pour partir de la fin)

  • collection modifiable: la taille et le contenu peuvent changer

Syntaxe: crochets [] et virgule qui sépare les éléments.

Attention: il peut y avoir répétition: cette collection n’est pas un ensemble.

1.4.1.1. Création, manipulation de listes#

liste_vide = []     # ou list()
planetes = ['venus', 'terre', 'mars']

planetes.append('mercure')      # ajout à la fin 
planetes
['venus', 'terre', 'mars', 'mercure']
planetes.extend(['jupyter', 'saturne'])
planetes
['venus', 'terre', 'mars', 'mercure', 'jupyter', 'saturne']
planetes.append(0)
planetes
['venus', 'terre', 'mars', 'mercure', 'jupyter', 'saturne', 0]
planetes.insert(0, 'mercure')   # insertion à la position 0 (1ère position)
planetes
['mercure', 'venus', 'terre', 'mars', 'mercure', 'jupyter', 'saturne', 0]
planetes.remove('mercure')      # supprime le 1er 'mercure'
planetes
['venus', 'terre', 'mars', 'mercure', 'jupyter', 'saturne', 0]
planetes.pop(0)       # supprime 1er élément et le renvoie
'venus'
planetes
['terre', 'mars', 'mercure', 'jupyter', 'saturne', 0]
del planetes[0]       # supprime 1er élément
planetes
['mars', 'mercure', 'jupyter', 'saturne', 0]
planetes[0] = 'terre' # remplace le 1er élément
planetes
['terre', 'mercure', 'jupyter', 'saturne', 0]
planetes = planetes + ['venus', 'terre', 'mars']   # plus lent qu'extend
planetes
['terre', 'mercure', 'jupyter', 'saturne', 0, 'venus', 'terre', 'mars']
planetes.count('terre')    # compte le nombre de 'terre'
2
planetes.index('terre')    # renvoie le 1er 'terre'
0

1.4.1.2. Appartenance, comparaison, copie#

print(planetes)
'uranus' in planetes # operateur in renvoie un booléen
['terre', 'mercure', 'jupyter', 'saturne', 0, 'venus', 'terre', 'mars']
False
plnts = planetes        # attention: création d'un synonyme, par défaut on a un comportement par référence
plnts.pop(-1)           # on retire le dernier élément de la liste: -1 fait référence au dernier élément 
print(planetes)
['terre', 'mercure', 'jupyter', 'saturne', 0, 'venus', 'terre']
print("Même liste (objet) ? ", plnts is planetes)
print("Même contenu ? ", plnts == planetes)
Même liste (objet) ?  True
Même contenu ?  True
p_cpy = planetes.copy()  # création d'une copies: pour copie recursive il faut utiliser deepcopy
print(p_cpy)
print("Même liste (objet) ? ", p_cpy is planetes)
print("Même contenu ? ", p_cpy == planetes)
['terre', 'mercure', 'jupyter', 'saturne', 0, 'venus', 'terre']
Même liste (objet) ?  False
Même contenu ?  True

1.4.1.3. Accès aux éléments: list slicing#

print(planetes)
planetes[0:2]     # 0 inclus et 2 exclu
['terre', 'mercure', 'jupyter', 'saturne', 0, 'venus', 'terre']
['terre', 'mercure']
planetes[:3]      # 0 implicite
['terre', 'mercure', 'jupyter']
planetes[3:]      # 4 = len(planetes) explicite
['saturne', 0, 'venus', 'terre']
planetes[-1]      # dernier élément similaire à planetes[len(planetes)-1]
'terre'
planetes[-2]      # avant-dernier
'venus'
planetes[::2]     # on fait des sauts de 2...
['terre', 'jupyter', 0, 'terre']
planetes[::-1] == list(reversed(planetes))
True

1.4.1.4. Trier#

planetes.sort?
planetes.remove(0)
print(planetes)
['terre', 'mercure', 'jupyter', 'saturne', 'venus', 'terre']
planetes.sort()     # sort in place: modification de la liste
planetes
['jupyter', 'mercure', 'saturne', 'terre', 'terre', 'venus']
len('jupyter')      # taille d'une chaine de caractères
7
planetes.sort(key=len)  # trie selon le nb de caractères de la planète
planetes
['terre', 'terre', 'venus', 'jupyter', 'mercure', 'saturne']
sorted?
sorted(planetes)
['jupyter', 'mercure', 'saturne', 'terre', 'terre', 'venus']

1.4.1.5. Compréhension de liste#

nums = [1, 2, 3, 4, 5]
cubes = []                    # on créé une liste vide
for num in nums:
    cubes.append(num**3)      # on ajoute les cubes
cubes
[1, 8, 27, 64, 125]
cubes = [x**3 for x in nums]  # même chose en plus court et lisible
cubes
[1, 8, 27, 64, 125]
pairs = [x for x in nums if x % 2 == 0] # version avec un filtre
pairs
[2, 4]
## exemple avec des listes imbriquées
L = [[0, 'a'], [2, 'b'], [3, 'c']]
result = [x*2 if type(x) == int else x*3 for elt in L for x in elt]
print(result)
[0, 'aaa', 4, 'bbb', 6, 'ccc']

Possible de faire de la compréhension de dict ou de set.

Méthodes utilisables:

## on définit cette fonction dont la syntaxe sera expliquée dans la suite 
def liste_methodes(obj): 
    return [func for func in dir(obj) if callable(getattr(obj, func)) and not func.startswith("__")]
print(liste_methodes(list))
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

1.4.1.6. Exercice: écrire une fonction ‘insort’#

Important: Si une fonctionnalité qui semble naturelle n’est pas présente dans la librairie de base il faut rechercher dans les modules standards de Python.

Par exemple pour insérer un élément dans une liste triée et garder cet ordre on utilisera le module bisect:

from bisect import insort
?insort
num = [10, 20, 40, 50]
print(num)

from bisect import insort
insort(num, 30)
num
[10, 20, 40, 50]
[10, 20, 30, 40, 50]

On considère une fonction qui permet d’insérer un élément x à sa bonne place dans une liste a supposée triée dans l’ordre croissant (on ne vérifie pas qu’elle est bien triée). Après l’insertion la liste a doit être encore triée dans l’ordre croissant.

  • Définir insort_linear qui code cette fonction en utilisant une recherche linéaire.

  • Définir insort_bsearch qui code cette fonction en faisant une recherche binaire (dichotomie).

  • Comparer les temps d’exécution avec la fonction insort du module bisect.

1.4.1.6.1. Réponse#
def insort_linear(a, x):
    """
    Insert item x in list a, and keep it sorted assuming a is sorted.
    Algo: linear search
    """
    for (i, y) in enumerate(a):
        if x < y:
            a.insert(i, x)
            break
    else:  # le cas où x est plus grand que tous les éléments de a  
        a.append(x)
from math import floor
def insort_bsearch(a, x):
    """
    Insert item x in list a, and keep it sorted assuming a is sorted.
    Algo: binary search
    """
    if x >= a[-1]: a.append(x) # cas particulier
    i, j = 0, len(a)-1
    while j > i+1:
        k = floor((i+j)/2)
        i, j = (i, k) if x < a[k] else (k, j) # attention aux parenthèses...
    a.insert(j, x)
n, x = 100000, 50000.2
liste = [ i for i in range(n) ]
%timeit insort_linear(liste, x)
1.37 ms ± 45.8 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
liste = [ i for i in range(n) ]
%timeit insort_bsearch(liste, x)
24.3 μs ± 603 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
liste = [ i for i in range(n) ]
%timeit insort(liste, x)
23.1 μs ± 169 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)
%%timeit   # mesure le temps de calcul de toute la cellule
liste = [ i for i in range(n) ]
insort(liste, x)
1.14 ms ± 15.2 μs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

1.4.2. Tuples ()#

  • collection hétérogène (similaire à list)

  • collection ordonnée, itérable (similaire à list)

  • collection non-modifiable (non-mutable), différence avec list

Syntaxe: virgule qui sépare les éléments et parenthèses () optionnelles.

Attention: il peut y avoir répétition: cette collection n’est pas un ensemble.

t = (2, 3, [3], 'c')  # création d'un tuple
t = 2, 3, [3], 'c'    # parenthèses optionnelles
print(t, type(t))
l = list(t)           # conversion en list
print(l, type(l)) 
(2, 3, [3], 'c') <class 'tuple'>
[2, 3, [3], 'c'] <class 'list'>
t + t                  # concaténation (comme pour les listes)
t * 3                  # répétition 3 fois
(2, 3, [3], 'c', 2, 3, [3], 'c', 2, 3, [3], 'c')
print(t)
(2, 3, [3], 'c')
a, b, c, d = t         # unpacking: très utilisé, on crée les variables a, b, c, d à partir du tuple
print(c)
[3]
i, j, k = 2, 3, 4      # initialisation des variables i, j et k via les tuples
print(*t)              # opéateur * pour exploser le tuple en arguments multiples 
2 3 [3] c
print(liste_methodes(t))
['count', 'index']

1.4.3. Chaînes de caractères ''#

Syntaxe: suite de caractères délimitée par une paire de

  • guillemets simples '

  • guillements doubles "

  • guillements triples '''

Un objet de type string s’utilise comme une liste de caractères (unicode) avec quelques méthodes supplémentaires spécifiques aux caractères (lower, upper, capitalize,…)

Il existe des modificateurs à placer devant les guillemets:

  • r pour indiquer que c’est une raw-string: utile pour les formules LaTeX

  • f pour indiquer que c’est une f-string (depuis python 3.6)

1.4.3.1. Type str#

s = str(2.1)                         # conversion en string
print(liste_methodes(s))             # les méthodes utilisables avec un string
['capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'removeprefix', 'removesuffix', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
s
'2.1'
x = float(s)                         # conversion en float
print('Nombre x: ' + str(x))         # concaténation
print('Nombre x: {:.5e}'.format(x))  # écriture scientifique avec 5 décimales
Nombre x: 2.1
Nombre x: 2.10000e+00
s.split?
st='  Bonjour le Monde     \nça va ?   \n  '
st.split()
['Bonjour', 'le', 'Monde', 'ça', 'va', '?']
print(st.upper())
  BONJOUR LE MONDE     
ÇA VA ?   
  
st.strip()
'Bonjour le Monde     \nça va ?'
print(st.startswith('B'))
print(st.strip().startswith('B'))     # on peut enchaîner les méthodes
False
True
st.split()
['Bonjour', 'le', 'Monde', 'ça', 'va', '?']

1.4.3.2. f-strings#

print(f"Valeur de x: {x:.4e}")  # usage plus facile avec les f-string
Valeur de x: 2.1000e+00
import locale, datetime
locale.setlocale(locale.LC_TIME, "en_US") 
name = 'Clément'
birthday = datetime.date(2010, 1, 19)
today = datetime.date.today()
age = int((today-birthday).days/365.25)
print(birthday)
print(f'Name: {name}, Age: {age}.')
print(f"Birthday: {birthday:%A, %B %d, %Y}.")
2010-01-19
Name: Clément, Age: 15.
Birthday: Tuesday, January 19, 2010.
locale.setlocale(locale.LC_TIME, "fr_FR") 
print(f'Date de naissance: {birthday:%A %d %B %Y}.')
Date de naissance: Mardi 19 janvier 2010.

Documentation supplémentaire: Doc python 3 et PEP-498.

1.4.4. Dictionnaires {}#

  • collection non-ordonnée, modifiable

  • collection itérable qui correspond à un tableau associatif, un ensemble de couple (clé, valeur):

    • une clé unique key de type: int, float, string ou tuples

    • une valeur value de type quelconque

Syntaxe: accolades {} et des couples key:value séparés par des virgules ,

Implémentation (algorithme interne): table de hashage.

Dans le module collections on trouve d’autres dictionnaires:

  • OrderedDict

  • defaultdict

1.4.4.1. Création, manipulation#

empty_dict = {}              # dictionnaire vide, ou bien dict()
type_planetes = {'terre': 'tellurique', 'mars':'tellurique', 'jupyter':'gazeuse'}
type_planetes = dict(terre='tellurique', mars='tellurique', jupyter='gazeuse')
type_planetes
{'terre': 'tellurique', 'mars': 'tellurique', 'jupyter': 'gazeuse'}
print(liste_methodes(type_planetes))
['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
type_planetes['terre']                    # renvoie la valeur correspondante 
type_planetes.get('terre', 'non trouvé')  # alternative qui fonctionne même si la clef n'est pas dans le dict
'tellurique'
print('mars' in type_planetes)    # appartenance pour les clés
print('tellurique' in type_planetes)   # pas pour les valeurs
True
False
type_planetes.items() # renvoie une vue (view) itérable, voir aussi keys() et values()
dict_items([('terre', 'tellurique'), ('mars', 'tellurique'), ('jupyter', 'gazeuse')])
for x in type_planetes.items():
    print(x)
('terre', 'tellurique')
('mars', 'tellurique')
('jupyter', 'gazeuse')

1.4.4.2. Modification#

type_planetes['saturne'] = 'gazeuse'                         # ajout d'une entrée
type_planetes['terre'] = ['tellurique', 'avec atmosphère' ]  # modification
print(type_planetes)
{'terre': ['tellurique', 'avec atmosphère'], 'mars': 'tellurique', 'jupyter': 'gazeuse', 'saturne': 'gazeuse'}
print(type_planetes)
{'terre': ['tellurique', 'avec atmosphère'], 'mars': 'tellurique', 'jupyter': 'gazeuse', 'saturne': 'gazeuse'}
del type_planetes['mars']        # détruit l'entrée mars
type_planetes.pop('saturne')     # détruit et renvoie la valeur
'gazeuse'
type_planetes.update({'uranus':'gazeuse', 'neptune':'gazeuse'})
type_planetes
{'terre': ['tellurique', 'avec atmosphère'],
 'jupyter': 'gazeuse',
 'uranus': 'gazeuse',
 'neptune': 'gazeuse'}

1.4.4.3. Parcourir un dictionnaire#

for key in type_planetes:
    print(key)
terre
jupyter
uranus
neptune
for key in type_planetes.keys():
    print(key)
terre
jupyter
uranus
neptune
for value in type_planetes.values():
    print(value)
['tellurique', 'avec atmosphère']
gazeuse
gazeuse
gazeuse
for key, value in type_planetes.items():
    print(key.capitalize(), ":", value)
Terre : ['tellurique', 'avec atmosphère']
Jupyter : gazeuse
Uranus : gazeuse
Neptune : gazeuse

1.4.5. Ensembles set#

  • collection hétérogène: composée d’objets distincts de types quelconques

  • collection non ordonnée, itérable

  • collection modifiable: la taille et le contenu peuvent changer

Syntaxe: mot-clé set (ou {}) et virgule qui sépare les éléments.

Implémentation (algorithme interne): table de hashage.

Pour un ensemble non modifiable on peut utiliser le type fronzenset.

1.4.5.1. Création, opérations#

ensemble_vide = set()                # seule syntaxe possible
lettres = set('bonjour')             # initialisation à partir d'une string  
print(lettres)

A = set([1, 2, 3, 4, 5, 6, 3, 2, 1]) # initialisation à partir d'une liste
print("A:", A)
{'n', 'r', 'u', 'o', 'b', 'j'}
A: {1, 2, 3, 4, 5, 6}
B = { 2, 3, 5, 7 }          # syntaxe possible sans le mot-clé set
B.add(11)
print("B:", B)
B: {2, 3, 5, 7, 11}
print("Intersection:\t", A & B)      
print("Union:\t\t", A | B)
print("Différence A\B:\t", A - B)
print("Différence B\A:\t", B - A)
Intersection:	 {2, 3, 5}
Union:		 {1, 2, 3, 4, 5, 6, 7, 11}
Différence A\B:	 {1, 4, 6}
Différence B\A:	 {11, 7}

1.4.5.2. Manipulation#

print(liste_methodes(A))
['add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']
A.update(lettres)          # ajout des éléments de lettres dans A
print(A)
A.difference_update(B)     # on supprime les éléments B de A
print(A)
{1, 2, 3, 4, 5, 6, 'n', 'r', 'u', 'o', 'b', 'j'}
{1, 4, 6, 'n', 'r', 'u', 'o', 'b', 'j'}
B.issubset(A)             
False
B.isdisjoint(A)
True
if not A & B: 
    print("Intersection vide !")
Intersection vide !

1.5. Compléments#

1.5.1. *args et **kwargs#

Opérateurs * et **: permettent d’exploser (unpacking) une liste ou un dictionnaire

l = [1, 6]
g(*l)        # appels possibles: g(*l, t=0.2) ou g(t=0.2, *l)
## ici c'est l'appel de g(1, 6, 0.5) car t a une valeur par defaut
3.5
d = {'a':1, 'b':6}
g(**d)       # même chose avec un dict
3.5

Utilisation de * et ** dans les arguments d’une fonction: permet de regrouper

  • des arguments non-nommés (dans une liste avec *, par convention appelée *args)

  • des arguments nommés (dans un dictionnaire avec **, par conversion **kwargs)

Très utilisé dans le module matplotlib: à connaitre pour bien comprendre la documentation.

Exemple d’utilisation de *args:

def gl(*args, t=0.5):
    if len(args) == 2:
        return args[0] + t*(args[1] - args[0])
    else: 
        print('Il faut 2 arguments')
gl(1, 6, t=0.2)        # attention l'appel gl(1, 6, 0.2) est invalide !
2.0

Exemple d’utilisation de **kwargs:

def gd(**kwargs):
    if len(kwargs) == 3:
        return kwargs['a'] + kwargs['t']*(kwargs['b'] - kwargs['a'])
    else: 
        print('Il faut 3 arguments')
gd(a=1, b=6, t=0.2)    # appel gd(1, 6, t=0.2) invalide
2.0

1.5.2. Documentation#