Python

Introduction

Langage interprété, orienté objet et dynamiquement typé pour

  • prototypage ou développement rapide
  • programmation web
  • contrôle de grandes applications
  • scripts, administration système
  • recherche et développement, calcul scientifique
  • sécurité informatique (et hacking)
  • et bien d'autres domaines ...

  • Extensible (C/C++/Fortran ou Java)

  • Libre
  • Mature (depuis 1991, communauté nombreuse et dynamique)
  • Syntaxe concise, claire et cohérente

Depuis quelques années, c'est le langage le plus populaire

La version officielle: CPython

  • Comme son nom l'indique, écrit en C
  • Extrêmement portable : Unix, Windows, MacOS, Android, iOS, systèmes embarqués
  • Interprété/compilé (bytecode)
  • Sûr (pas de pointeurs, gestion automatique de la mémoire)
  • Nombreuses bibliothèques (réseau, bases de données, interfaces graphiques, son, vidéo ...)
  • Extensible C/C++/Fortran (Cython)

Il existe une version en Java: JPython ou Jython

  • Totalement intégré avec Java
  • Compilation directe en bytecode Java
  • Importation directe des classes Java
  • Héritage depuis les classes Java
  • Support des JavaBeans, des applettes, des servlettes
  • Même langage mais différences actuellement dans les modules d'extension

Bref historique

  • Première version en février 1991 (Guido van Rossum)
  • Version 2.0 en 2000
  • Aujourd'hui : version 2.7.14
  • Fork : Version 3.0 en décembre 2008, (quelques incompatibilités avec les précédentes, 3.6.2 actuellement)
  • Versions embarquées (téléphones portables : Nokia série 6, Palm .ndroid, iOS..)
  • Stackless Python (microthreads, pour faciliter la programmation concurrente)
  • Autres implémentations : IronPython (.NET), ActivePython, PyPy ...
  • Distributions avec paquetages préinstallés, comme Anaconda

Cours en version 2, TP possibles en version 3.

Python 2.7 sera continué au moins jusqu'en 2024. Il n'y aura pas de 2.8.

Qui utilise Python ?

  • Google ("Python were we can, C++ where we must")
  • Yahoo
  • Microsoft
  • Les distributions de Linux : Red Hat, Ubuntu, ...
  • la NASA
  • EDF (code Aster)
  • LibreOffice (depuis OpenOffice2.0),Vim, Mozilla ...
  • Communauté scientifique (Sage ...)
  • Et bien d'autres ...

Caractéristiques du langage

  • Tout est un objet
  • Modules, classes, fonctions
  • Exceptions
  • Typage dynamique, polymorphisme
  • Surcharge des opérateurs
  • Syntaxe claire et intuitive : mix C/Algol, indentation pour délimiter les blocs (pseudo-code exécutable)
  • Supporte plusieurs styles de programmation : impératif, objet, fonctionnel ...

Types de données

  • Nombres: entiers, entiers (très) longs, réels, flottants, complexes
  • Chaînes de caractères (immuables)
  • Tuples, listes et dictionnaires (conteneurs)
  • D'autres types natifs créés par des extensions en C (expressions régulières, descripteurs de fichiers, sockets...)
  • Instances de classes définies en Python

Le code est compact et lisible

Java :

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Python 2 :

print "Hello, World!"

Python 3 :

print ("Hello, World!")

Exemple : QuickSort en deux lignes !

In [4]:
def q(L): 
  if len(L)<= 1: return L 
  return q([x for x in L[1:] if x<L[0]])+[L[0]]+q([y for y in L[1:] if y>=L[0]])
In [5]:
L = [78, 46, 63, 20, 53, 10, 26, 52, 41, 54, 81, 75, 49, 21, 80, 60, 58, 56, 86,40, 95, 92, 0, 4, 77, 12, 5, 59, 90, 57, 71, 3, 
     65, 27, 97, 89, 19, 38, 15, 85, 6, 62, 11, 33, 67, 61, 73, 44, 50, 17, 94, 48, 43, 34, 55, 24, 87, 70, 2, 16, 42, 25, 37, 
     68, 88, 30, 23, 7, 83, 74, 84, 39, 32, 98, 99, 22, 1, 45, 82, 69, 96, 31, 51, 91, 14, 13, 66, 36, 9, 18, 8, 79, 47, 72, 29, 
     76, 93, 64, 28, 35]
In [12]:
print q(L)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

Explication

In [8]:
print len(L) # c'est la longueur de la liste
100

In [9]:
print L[0] # les indices commencent à 0
78

In [10]:
print L[98:] # après 98 (inclus)
[28, 35]

In [11]:
print L[:3] # avant 3 (non inclus)
[78, 46, 63]

In [13]:
print [x for x in L if x<5] # Compréhension de liste
[0, 4, 3, 2, 1]

In [14]:
print [1,2]+[3,4] # concaténation des listes
[1, 2, 3, 4]

Évidemment, ce n'est pas une implémentation efficace de QuickSort ! Mais ça permet de vérifier que l'algorithme donne un résultat correct.

Le tri est implémenté très efficacement en Python (fonction sorted, ou méthode sort qui trie en place):

In [20]:
print sorted(L)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

In [21]:
L.sort()
In [22]:
print L
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

Premiers pas

Python est un langage interprété, et il est important d'apprendre à l'utiliser comme tel.

L'utilisation la plus basique consite à entrer la commande python dans un terminal. On rentre dans un interpréteur qu'on peut utiliser comme une calculette :

[jyt@liszt jyt]$ python
Python 2.6.1 (r261:67515, Dec 25 2008, 16:25:51)
[GCC 3.4.1 (Mandrakelinux 10.1 3.4.1-4mdk)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 2**100
1267650600228229401496703205376L

On peut aussi écrire des scripts. La première ligne doit être #! suivi du chemin vers l'interpréteur:

[jyt@liszt jyt]$ cat a.py
#!/usr/bin/python
print 2**100
[jyt@liszt jyt]$ chmod +x a.py
[jyt@liszt jyt]$ ./a.py
1267650600228229401496703205376

On peut faire exécuter des fichiers

[jyt@liszt jyt]$ cat b.py
print 2**100
[jyt@liszt jyt]$ python b.py
1267650600228229401496703205376

ou encore, envoyer une commande à l'interpréteur depuis le shell

[jyt@liszt jyt]$ echo "print 2**100"|python
1267650600228229401496703205376

Avec un langage interprété, on peut tester les instructions une à une avant de les incorporer dans le programme

On aura donc .un interpréteur de test dans une fenêtre, et un éditeur (vim, emacs, gedit ...) dans une autre (éviter gedit).

La fonction help(objet) affiche l'aide en ligne d'un objet

On peut accéder à la liste des attributs d'un objet avec la commande dir :

In [23]:
help(sorted)
Help on built-in function sorted in module __builtin__:

sorted(...)
    sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list


In [27]:
print dir("toto")
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

Environnements graphiques

Pratique pour débuter : l'IDE idle, développé en pur Python à titre de démonstration, et fourni avec la distribution standard (à compiler avec tkInter, ou paquetage à installer).

Le nec plus ultra: jypyter, nouveau nom de iPython, avec son notebook. C'est ce qu'on utilise ici, et ce sera la norme pour les TP.

Il existe aussi un plugin Python (PyDev) pour Eclipse, et d'autres IDE du même genre (Eric).

Pour ce cours et les suivants (cryptographie, combinatoire), leur emploi est déconseillé.

Conventions

Essentiellement comme en C : indices à partir de 0, = et ==, opérateurs arithmétiques +,-,/,,*, "guillemets" pour les chaînes, opérations bit à bit >>,<<,&,|.

Par contre, les opérateurs logiques sont or et and, et les booleens sont True et False.

In [32]:
a=2
b=a+1
print a,b, a+b, a*b, a**b, a/b
2 3 5 6 8 0

In [30]:
a<=4
Out[30]:
True
In [31]:
a>5 or b>3
Out[31]:
False

Il n'y a pas de pointeurs. Mais les variables sont des références à des objets. Si b = a, une modification de a peut affecter b :

In [33]:
a=2
b=a
a=3
print b
2

Jusqu'ici, RAS. Mais avec des objets plus complexes ...

In [34]:
a=[1,2,3]
b=a
a[1]=5
print b
[1, 5, 3]

On peut éviter ce problème avec le module copy et ses fonctions copy et deepcopy, qui permettent de réaliser des copies partielles ou totales d'un objet.

On en a rarement besoin, il suffit généralement d'avoir conscience du problème.

In [36]:
import copy
x=[1,2,3]
y=copy.copy(x)
print 'y = ', y
x[1]=5
print 'x = ', x
print 'y = ', y
y =  [1, 2, 3]
x =  [1, 5, 3]
y =  [1, 2, 3]

In [37]:
u=[1,[2,3],4]
v=copy.copy(u)
u[0]=6
print 'u = ', u
print 'v = ', v
u[1][0]=7
print 'u = ', u
print 'v = ', v
u =  [6, [2, 3], 4]
v =  [1, [2, 3], 4]
u =  [6, [7, 3], 4]
v =  [1, [7, 3], 4]

In [38]:
p=[1, [2,[3,4],5], [6,7]]
q=copy.deepcopy(p)
p[1][1][0]=8
print 'p = ', p
print 'q = ', q
p =  [1, [2, [8, 4], 5], [6, 7]]
q =  [1, [2, [3, 4], 5], [6, 7]]

Blocs et indentation

Pas d'accolades ou de begin/end. C'est l'indentation qui détermine la structure des blocs :

In [40]:
for i in range(0,256):
    if chr(i).isalnum():
        print i, chr(i), " ",
    else: pass
48 0   49 1   50 2   51 3   52 4   53 5   54 6   55 7   56 8   57 9   65 A   66 B   67 C   68 D   69 E   70 F   71 G   72 H   73 I   74 J   75 K   76 L   77 M   78 N   79 O   80 P   81 Q   82 R   83 S   84 T   85 U   86 V   87 W   88 X   89 Y   90 Z   97 a   98 b   99 c   100 d   101 e   102 f   103 g   104 h   105 i   106 j   107 k   108 l   109 m   110 n   111 o   112 p   113 q   114 r   115 s   116 t   117 u   118 v   119 w   120 x   121 y   122 z  

Le point-virgule en fin de ligne est optionnel. On peut l'utiliser pour mettre plusieurs instructions par ligne :

In [41]:
 x='abra'; y='cadabra'; z=x+y; print z
abracadabra

On peut utiliser des \ pour continuer une instruction sur la ligne suivante :

In [43]:
print 'abra' \
+'cadabra'
abracadabra

Il est parfois plus simple (et recommandé) d'utiliser des parenthèses :

In [44]:
print ('abra'
       + 'cadabra')
       
abracadabra

On ne doit pas mélanger espaces et tabulations. La convention la plus répandue est d'indenter de 4 espaces.

Structures de contrôle

Les tests (conditionnelles) : if-elif-else, while

if condition: 
    faire_si_vrai()
elif autre_condition: 
    faire_autre_truc()
else: 
    faire_si_tout_faux()

while condition: 
    iteration()
    if fini: break     # sort de la boucle
    if pouet: continue # saute ce qui suit
    autre_chose()

condition n'est pas forcément un booléen True/False.

En Python, tout objet est "Vrai" sauf False, 0, "", None, [], (), {}.

On pourra donc écrire simplement

if i au lieu de

if i != 0.

Les opérateurs booléens s'écrivent or, and, not.

Le test d'égalité est a == b, et sa négation a != b.


Sémantique :

  • x or y équivaut à si x alors x sinon y
  • x and y équivaut à si x alors y sinon x

Par exemple au lieu de

if a !=0: x = 666/a
else: x = 42

on peut écrire

x = (a and 666/a) or 42

On peut aussi combiner test et affectation :

In [45]:
a = 0
x = 5*a if a>0 else 42 # equivalent de x = (a>0) ? (5*a:42)
x
Out[45]:
42
In [47]:
a=2
x = 5*a if a>0 else 42
x
Out[47]:
10

Boucles et itérations

Les itérations portent sur des listes, ou plus généralement sur des objets itérables : une seule syntaxe, for i in quelquechose:

for element in liste:
    manip(element) 
    if foo(element): break
    if quux(element): continue
    chose(element)

Pour itérer sur des entiers on engendre leur liste au moyen de la fonction range

Syntaxe : range([debut=0],fin,[pas=1]

In [52]:
print range(2,10)
print range(10)
print range(0,10,2)
print range(10,0,-1)
[2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 2, 4, 6, 8]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

In [53]:
for i in range(10):
    print 2*i+1,
1 3 5 7 9 11 13 15 17 19

La fonction range construit une liste en mémoire (en python 2). Il est plus efficace d'utiliser xrange, un itérateur qui engendre les valeurs une à une.

In [56]:
ll = xrange(10)
In [57]:
ll
Out[57]:
xrange(10)
In [59]:
list(ll)
Out[59]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Les types

TYpe numériques : entiers et flottants

>>> 2+3*4
14
>>> 1<<8        # bit shift, aussi >>, &, |, ^ (XOR)
256
>>> 5**3    # puissance 3
125
>>> 42%10    # modulo 10
2
>>> 2**(1/12)    
1
>>> 1/12        # attention à la division entière en python 2. En python 3, 1/12 est 0.08333333333333333
0
>>> 2**(1.0/12)    # en flottant, ça marche
1.0594630943592953
>>> 2**200    # entiers arbitrairement longs
1606938044258990275541962092341162602522202993782792835301376L

On voit que Python supporte la multiprécision (utile pour la cryptographie, entre autres).

Les fonctions mathématiques de base sont dans le module math :

>>> import math
>>> math.cos(math.pi/3)
0.50000000000000011

On dispose de fonctions de conversion entre bases

>>> int("0256")
256
>>> int("1101101",2)
109
>>> hex(_)
'0x6d'
>>> int(_,16)
109
>>> bin(109)
'0b1101101'
In [62]:
044 # Octal en python 2. En python 3, ce serait 0o44
Out[62]:
36
In [63]:
0xff # hexadécimal
Out[63]:
255
In [64]:
0b1101 # binaire
Out[64]:
13
In [65]:
0o44 # marche aussi en python 2 
Out[65]:
36

Pour les flottants en multiprécision, c'est le module decimal qu'il faut utiliser :

In [5]:
from decimal import *
getcontext()
Out[5]:
Context(prec=200, rounding=ROUND_HALF_EVEN, Emin=-999999999, Emax=999999999, capitals=1, flags=[Rounded, Inexact], traps=[InvalidOperation, Overflow, DivisionByZero])
In [6]:
getcontext().prec = 200
r = Decimal(2).sqrt()
In [7]:
print r
1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462309122970249248360558507372126441214970999358314132226659275055927557999505011527820605715

In [8]:
r
Out[8]:
Decimal('1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727350138462309122970249248360558507372126441214970999358314132226659275055927557999505011527820605715')

les chaînes

On peut utiliser deux types de guillemets. 'toto' et "toto" sont équivalentes.

Permet d'écrire "l'apostrophe" ou 'les "guillemets"'.

Les triples guillemets """...""" ou '''...''' permettent d'écrire sur plusieurs lignes.

Une chaîne qui traîne au milieu d'un programme est vue comme un commentaire.

Placées aux endroits idoines, elles permettent de générer automatiquement la documentation. Les autres commentaires commencent par un #.

In [94]:
a = 'toto'; b = "toto"
a == b
Out[94]:
True
In [99]:
def nothing():
    '''Do nothing.
    
    Usage: nothing()'''
    pass
In [100]:
help(nothing)
Help on function nothing in module __main__:

nothing()
    Do nothing.
    
    Usage: nothing()


L'opérateur de concaténation est le +, on peut aussi répéter une chaîne en la multipliant par un entier :

In [101]:
'abra'+"""cadabra"""
Out[101]:
'abracadabra'
In [102]:
'bla'*3
Out[102]:
'blablabla'

Le contenu des chaînes est accessible via une syntaxe de type tableau :

In [103]:
s = 'shaviro rotantacha shamipataro robrulapatacha'
s[1]
Out[103]:
'h'
In [135]:
print s[7:15] # une tranche
print s[::2]  # un caractère sur deux (:: signifie que début et fin sont les valeurs par défaut)
print s[::-1] # retournée
print s[19:3:-2] # de 19 à 2 par pas de 2
print s[-1] # indices circulaires : -1 est le dernier
 rotanta
saiortnah hmptr orlptca
ahcatapalurbor oratapimahs ahcatnator orivahs
sactao r
a

A la différence des listes/tableaux, les chaînes sont immuables :

In [105]:
s[0] = 'b'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-105-b157954a8563> in <module>()
----> 1 s[0] = 'b'

TypeError: 'str' object does not support item assignment

On dispose de nombreux outils pour construire des chaînes :

In [118]:
"abc%sdef%d" % ("BLA",4) # formatage style "printf"
Out[118]:
'abcBLAdef4'
In [119]:
"abc{0:s}sdef{1:d}".format("BLA",4) # nouvelle syntaxe
Out[119]:
'abcBLAsdef4'
In [120]:
"%08X" % 42069    # conversions de base (ici, hexa sur 8 caractères avec lettres majuscules)
Out[120]:
'0000A455'
In [121]:
"%%%s" % "d" % 12 # On peut aussi écrire du code illisible (exercice : comprendre le résultat) 
Out[121]:
'12'

Les chaines ont de nombreuses méthodes très pratiques (essayer dir('')).

In [122]:
print "rac" in "abracadabra" # équivaut à 'abracadabra'.__contains__('rac') 
print len("toto")  # équivaut à 'toto'.__len__()
print "toto".upper()
print  "    \t  x yz   \n".strip()
print "albert".startswith("al")
print  "salamalec".replace("al","ila")
print "l'an de l'ananas".count("an")
True
4
TOTO
x yz
True
silaamilaec
3

La plupart des objets peuvent être convertis en strings de deux manières. str(obj) est la chaîne affichée par print obj, et repr(obj) est celle affichée par l'interpréteur quand on entre obj. Si possible, repr renvoie une chaîne qui peut être reconvertie en objet par la fonction eval.

In [127]:
str(2**100)
Out[127]:
'1267650600228229401496703205376'
In [133]:
repr(2**100)
Out[133]:
'1267650600228229401496703205376L'
In [134]:
eval(_)
Out[134]:
1267650600228229401496703205376L

Le module string offre quelques fonctionalités supplémentaires (pour la plupart obsolètes, voir cependant maketrans et translate, ainsi que les chaines punctuation, whitespace, ...).

La gestion des encodages a été une des pricipales motivations pour python 3.

In [137]:
s = '我的表弟的马只在周日吃了干草' # copié-collé depuis une page en utf-8
In [138]:
print s
我的表弟的马只在周日吃了干草

In [143]:
s.decode('utf8') # renvoie une chaîne unicode
Out[143]:
u'\u6211\u7684\u8868\u5f1f\u7684\u9a6c\u53ea\u5728\u5468\u65e5\u5403\u4e86\u5e72\u8349'
In [144]:
t = _ # _ est le dernier résultat évalué
print t # print comprend l'unicode
我的表弟的马只在周日吃了干草

In [145]:
s
Out[145]:
'\xe6\x88\x91\xe7\x9a\x84\xe8\xa1\xa8\xe5\xbc\x9f\xe7\x9a\x84\xe9\xa9\xac\xe5\x8f\xaa\xe5\x9c\xa8\xe5\x91\xa8\xe6\x97\xa5\xe5\x90\x83\xe4\xba\x86\xe5\xb9\xb2\xe8\x8d\x89'
In [146]:
s.encode('hex')
Out[146]:
'e68891e79a84e8a1a8e5bc9fe79a84e9a9ace58faae59ca8e591a8e697a5e59083e4ba86e5b9b2e88d89'
In [147]:
s.encode('base64')
Out[147]:
'5oiR55qE6KGo5byf55qE6ams5Y+q5Zyo5ZGo5pel5ZCD5LqG5bmy6I2J\n'
In [152]:
s[0]
Out[152]:
'\xe6'
In [153]:
print s[0]
In [154]:
t[0]
Out[154]:
u'\u6211'
In [155]:
print t[0]

Listes et tuples

listes

En Python, liste=tableau. Les indices commencent à 0. On crée une liste en indiquant ses éléments entre [], séparés par des virgules.

On accède aux éléments et aux tranches par la syntaxe déjà vue dans le cas des chaînes.

In [160]:
ll = ['toto', 42, ['ga', 'bu', 'zo', 'meu'], 999] # Une liste peut contenir n'importe quoi
print ll[0]
print ll[2]
toto
['ga', 'bu', 'zo', 'meu']

In [161]:
print ll[2][-1]
meu

In [162]:
ll.append('foo') # la méthode append insère un élément à la fin
ll
Out[162]:
['toto', 42, ['ga', 'bu', 'zo', 'meu'], 999, 'foo']
In [163]:
ll.pop() # et pop() le dépile
Out[163]:
'foo'
In [164]:
ll
Out[164]:
['toto', 42, ['ga', 'bu', 'zo', 'meu'], 999]
In [166]:
mm = [1,2,3]; nn = [4,5,6] # concaténation avec +
mm+nn
Out[166]:
[1, 2, 3, 4, 5, 6]
In [167]:
mm*3 # répétition
Out[167]:
[1, 2, 3, 1, 2, 3, 1, 2, 3]
In [168]:
mm.extend(nn) # plus efficace
In [169]:
mm
Out[169]:
[1, 2, 3, 4, 5, 6]
In [170]:
42 in ll # test de présence
Out[170]:
True
In [171]:
ll[-1] = 'bla' # les listes sont mutables
ll
Out[171]:
['toto', 42, ['ga', 'bu', 'zo', 'meu'], 'bla']
In [173]:
ll[2].sort() # même à l'intérieur d'un objet
ll
Out[173]:
['toto', 42, ['bu', 'ga', 'meu', 'zo'], 'bla']
In [174]:
del(ll[2]) # on peut détruire n'importe quelle entrée
ll
Out[174]:
['toto', 42, 'bla']
In [175]:
ll.reverse() # renverser la liste en place
ll
Out[175]:
['bla', 42, 'toto']
In [183]:
s = 'abracadabra' # Les chaînes peuvent être converties en listes de caractères
list(s)
Out[183]:
['a', 'b', 'r', 'a', 'c', 'a', 'd', 'a', 'b', 'r', 'a']
In [185]:
' '.join(_) # méthode join de la chaine ' ' (espace)
Out[185]:
'a b r a c a d a b r a'
In [184]:
t = 'Il est plus facile de se laver les dents dans un verre à pied que de se laver les pieds dans un verre à dents'
tt = t.split() # par défaut, coupe selon les espaces et autres blancs
print tt
['Il', 'est', 'plus', 'facile', 'de', 'se', 'laver', 'les', 'dents', 'dans', 'un', 'verre', '\xc3\xa0', 'pied', 'que', 'de', 'se', 'laver', 'les', 'pieds', 'dans', 'un', 'verre', '\xc3\xa0', 'dents']

tuples

Les tuples sont comme des listes, mais immuables. Ils sont délimités par des parenthèses.

Le tuple vide est (), celui à un élément x est (x,). La virgule crée automatiquement un tuple.

In [186]:
t = 1,2,3
t
Out[186]:
(1, 2, 3)
In [187]:
t[0]=7
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-187-a1f3206a315a> in <module>()
----> 1 t[0]=7

TypeError: 'tuple' object does not support item assignment
In [188]:
tuple('abracadabra')
Out[188]:
('a', 'b', 'r', 'a', 'c', 'a', 'd', 'a', 'b', 'r', 'a')
In [193]:
vide =()
vide
Out[193]:
()
In [195]:
singleton=(42,)
singleton
Out[195]:
(42,)
In [196]:
(42)
Out[196]:
42
In [198]:
list(vide)
Out[198]:
[]
In [199]:
list(singleton)
Out[199]:
[42]
In [201]:
a = 2; b= 7
print a,b
a,b = b,a # echange en une instruction
print a,b
2 7
7 2

Dictionnaires

Les dictionnaires sont des tables de hachage, implémentées de manière très efficace.

Un dictionnaire est défini par des couples clef:valeur entre accolades.

Pour pouvoir être hachées, les clefs doivent être immuables : entiers, chaînes, tuples ...

In [219]:
bazar = {"e": 2.71828, "jaune": (255,255,0), "vrai": True, 10: "dix", "liste": [4, 2, {False: "faux"}], (0,0):"origine"}

bazar["liste"]  
Out[219]:
[4, 2, {False: 'faux'}]
In [220]:
bazar.keys()
Out[220]:
['jaune', (0, 0), 'e', 10, 'liste', 'vrai']
In [221]:
bazar.values()
Out[221]:
[(255, 255, 0), 'origine', 2.71828, 'dix', [4, 2, {False: 'faux'}], True]
In [222]:
bazar.items()
Out[222]:
[('jaune', (255, 255, 0)),
 ((0, 0), 'origine'),
 ('e', 2.71828),
 (10, 'dix'),
 ('liste', [4, 2, {False: 'faux'}]),
 ('vrai', True)]
In [9]:
d = dict([(1,"un"), (2,"deux")]) # On peut créer un dictionnaire avec une liste de couples
d
Out[9]:
{1: 'un', 2: 'deux'}
In [224]:
for k in d: print k, # on itère sur les clefs
1 2

In [225]:
del bazar["liste"]
print bazar
{'jaune': (255, 255, 0), (0, 0): 'origine', 'e': 2.71828, 10: 'dix', 'vrai': True}

In [227]:
fiche = {'nom':'Toto', 'age':7} # On peut utiliser un dictionnaire pour formater avec %
"Je m'appelle %(nom)s et j'ai %(age)d ans" % fiche
Out[227]:
"Je m'appelle Toto et j'ai 7 ans"
In [230]:
s = "le cheval de mon cousin ne mange du foin que le dimanche"
# Pour compter le nombre d'occurences de chaque caractère (et on peut faire encore plus compact)
d = {}
for c in s:
    if c in d: d[c] +=1
    else: d[c] = 1
print d
{'a': 3, ' ': 11, 'c': 3, 'e': 8, 'd': 3, 'g': 1, 'f': 1, 'i': 3, 'h': 2, 'm': 3, 'l': 3, 'o': 3, 'n': 6, 'q': 1, 's': 1, 'u': 3, 'v': 1}

In [233]:
# La version plus compacte nous amène aux sujets suivants
print {c:s.count(c) for c in set(s)}
{'a': 3, ' ': 11, 'c': 3, 'e': 8, 'd': 3, 'g': 1, 'f': 1, 'i': 3, 'h': 2, 'm': 3, 'l': 3, 'o': 3, 'n': 6, 'q': 1, 's': 1, 'u': 3, 'v': 1}

Ensembles (sets)

Un ensemble, c'est un dictionnaire dont les clefs n'ont pas de valeur :

In [236]:
E = {2,3,5,7,11,13}
F = {5,7,17,19,23}
E.union(F)
Out[236]:
{2, 3, 5, 7, 11, 13, 17, 19, 23}
In [237]:
E.intersection(F)
Out[237]:
{5, 7}
In [239]:
F.difference(E)
Out[239]:
{17, 19, 23}
In [242]:
print set(s)
set(['a', ' ', 'c', 'e', 'd', 'g', 'f', 'i', 'h', 'm', 'l', 'o', 'n', 'q', 's', 'u', 'v'])

Listes, tuples, dictionnaires et ensembles en compréhension

C'est une syntaxe très expressive, empruntée à Haskell. L'idée est de construire une liste et écrivant entre crochets la description de ce qu'on veut y mettre :

[x for x in truc if bidule(x)]
In [243]:
print  [x for x in s if x in list('aeiouy')]
['e', 'e', 'a', 'e', 'o', 'o', 'u', 'i', 'e', 'a', 'e', 'u', 'o', 'i', 'u', 'e', 'e', 'i', 'a', 'e']

In [244]:
print {x:s.count(x) for x in set(s)}
{'a': 3, ' ': 11, 'c': 3, 'e': 8, 'd': 3, 'g': 1, 'f': 1, 'i': 3, 'h': 2, 'm': 3, 'l': 3, 'o': 3, 'n': 6, 'q': 1, 's': 1, 'u': 3, 'v': 1}

In [245]:
print [x for x in range(0,256) if chr(x).isdigit()]
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57]

In [246]:
# Crible d'Eratosthène
noprimes = [j for i in range(2, 8) for j in range(i*2, 50, i)]
primes = [x for x in range(2, 50) if x not in noprimes]
In [247]:
print primes
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

In [248]:
print noprimes
[4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 10, 15, 20, 25, 30, 35, 40, 45, 12, 18, 24, 30, 36, 42, 48, 14, 21, 28, 35, 42, 49]

In [249]:
# Extraire les variables affectées dans un fichier de configuration
d=dict([kv.split("=",1) 
       for kv in open("/etc/libuser.conf").read().split("\n")
       if "=" in kv and not kv.strip().startswith("#")])
In [250]:
d
Out[250]:
{'LU_GIDNUMBER ': ' %u',
 'LU_GROUPNAME ': ' %n',
 'LU_USERNAME ': ' %n',
 'create_modules ': ' files shadow',
 'crypt_style ': ' blowfish',
 'default_useradd ': ' /etc/default/useradd',
 'login_defs ': ' /etc/login.defs',
 'modules ': ' files shadow'}

Les fonctions

On définit une fonction par le mot clef def, suivi du nom de la fonction, des arguments entre parenthèses, et de ":". Si la fonction doit renvoyer une valeur, il faut utiliser return

In [11]:
def f(x,y):
    return 2*x+y

print f(2,5)
print f('bla','blu')
9
blablablu

In [252]:
# Fonction récursive
def factorielle(n):
    if n<2: return 1
    return n*factorielle(n-1)

factorielle(100)
Out[252]:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
In [253]:
def aire(x1,y1,x2,y2):
    delta_x=x2-x1
    delta_y=y2-y1
    return abs(delta_x*delta_y)

aire(1,2,6,8)
Out[253]:
30

Les fonctions peuvent avoir des argument nommés avec des valeurs par défaut :

In [255]:
def aire(a,b,x2=None,y2=None): 
    if x2: x1,y1=a,b
    else: (x1,y1),(x2,y2)=a,b
    return abs((x2-x1)*(y2-y1))
print aire(1,2,6,8)
print aire((1,2), (6,8))
30
30

Interdit : fonction(param=valeur,autreparam)

On peut avoir un nombre variable d'arguments :

In [257]:
def printf(format_string, *args):
    print format_string % args

printf("%s: %d","trois",3)

liste=["riri", "fifi", "loulou", 17]
printf("%s + %s + %s = %d",*liste)
trois: 3
riri + fifi + loulou = 17

L'opérateur * extrait les éléments d'une liste (seulementdans un appel de fonction).

toto(a,b,c) = toto(*[a,b,c])

On peut aussi avoir des argument nommés arbitraires :

In [258]:
def apply_font(font="Helvetica", size=12, **styles):
    print "Font name is %s."%font
    print "Font size is %d."%size
    print "Extra attributes:"
    for attrname,attrvalue in styles.items():
        print "\t%s: %s"%(attrname,str(attrvalue))

apply_font(size=24,color="red",background="black")
Font name is Helvetica.
Font size is 24.
Extra attributes:
	color: red
	background: black

In [259]:
apply_font(**{"color": "red", "background": "black"})
Font name is Helvetica.
Font size is 12.
Extra attributes:
	color: red
	background: black

La variable styles est vue comme un dictionnaire, et l'opérateur ** en extrait les couples clef=valeur.

Une fonction peut renvoyer plusieurs valeurs. Il suffit de retourner un tuple. Ses éléments peuvent alors être affectées en une seule instruction à différentes variables :

In [261]:
def division(a,b):
    return a/b, a%b
q,r = division (26,3)
print q,r
8 2

Lorsqu'on passe un paramètre à une fonction, une référence à l'objet passé est ajoutée à la portée locale de la fonction.

Lorsqu'on modifie l'objet en place, la fonction voit les modifications, car le paramètre local est une référence à l'objet qu'elle voit.

Lorsque l'on affecte une nouvelle valeur au paramètre local, l'objet ne change pas. Le nom du paramètre est maintenant une référence à un objet différent, mais cela n'affecte pas le contenu de l'objet d'origine.

In [263]:
def f(a, b, c):
    a += "!!!"
    b.append(5)
    c = []

txt = "???"
list1 = [1, 2, 3]
list2 = [1.0, 2.0]
f(txt, list1, list2)
print "txt =", txt
print "list1 =", list1
print "list2 =", list2
txt = ???
list1 = [1, 2, 3, 5]
list2 = [1.0, 2.0]

Lorsque la fonction est invoquée, elle crée trois noms locaux qui contiennent des références aux objets passés passés: a = txt, b = list1, c = list2

La ligne a + = "!!!" attribue une nouvelle référence d'objet au nom local a. Elle ne change pas la chaîne in situ. (Les chaînes ne peuvent pas être modifiées en tout parce qu'elles sont des objets immuables.

La ligne b.append (5) ajoute une valeur à l'objet pointé par b. C'est le même objet que f connaît comme list1.

La ligne c = [] attribue une nouvelle référence d'objet au nom local c. Elle ne modifie pas l'objet c mentionné précédemment

Fonctions anonymes

Il est inutile de donner un nom à une fonction qu'on n'utilise qu'une fois. Le mot clef lambda (emprunté au Lisp, basé sur le lambda-calcul) permet de définir une fonction anonyme :

In [264]:
lambda x: x*x
Out[264]:
<function __main__.<lambda>>
In [265]:
f = lambda x:x*x
f(3)
Out[265]:
9

Elles s'utilisent pricipalement avec les constructions map,filter,reduce, issues de la programmation fonctionnelle :

In [266]:
map(lambda x: x**2, range(1,10)) # liste des carrés des entiers de 1 à 9
Out[266]:
[1, 4, 9, 16, 25, 36, 49, 64, 81]
In [267]:
filter(lambda x: x%2, range(1,10)) # sélectionne les nombres impairs (x%2=0, qui est ""faux" si x est pair)
Out[267]:
[1, 3, 5, 7, 9]
In [268]:
reduce(lambda x,y: x*y, range(1,101)) # calcule 100!
Out[268]:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

reduce(f(x,y),liste) applique f aux deux premiers éléments, puis au résultat et au troisième, et ainsi de suite...

Ces constructions sont dépréciées. map(f,ll) peut être avantageusement remplacée par

[f(x) for x in ll]. Les deux autres sont reléguées au module functools en python 3. Elles sont considérées comme peu lisibles, mais restent commodes quand on travaille en mode interprété.

Le typage en Python

Typage dynamique : les types sont implicitement polymorphes

On peut surcharger les opérateurs. Par exemple, + est automatiquement défini sur les objets qui possèdent une méthode spéciale

__add__

Duck typing : Connaître le type d'un objet n'a pas d'importance, il faut seulement s'assurer qu'on peut lui appliquer les traitements souhaités

"si ça marche comme un canard et si ça cancane comme un canard, alors ce doit être un canard (et si c'est une oie, on doit pouvoir faire avec)".

Utiliser la coertion si un objet doit être d'un type particulier str(x) plutôt que de demander isinstance(x,str).

EAFP : "It's easier to ask forgiveness than permission". Utiliser le mécanisme de gestion des exceptions (try ... except) pour déterminer le traitement approprié.

In [269]:
print type(5), type(5.0), type("abra"),type([1,2])
<type 'int'> <type 'float'> <type 'str'> <type 'list'>

In [270]:
2+'2'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-270-520cfc4f4ec7> in <module>()
----> 1 2+'2'

TypeError: unsupported operand type(s) for +: 'int' and 'str'
In [271]:
isinstance('toto',str)
Out[271]:
True
In []: