Pourquoi python ? Productivité + performances
On dispose des opérateurs classiques sur ces types de données.
a = 0
b = "hello"
c = 6.789
print(c)
print(a,b,c,end=".")
print(type(a))
a = a + c
print(a)
print(type(a))
# petite subtilité
print(10/2)
print(10//2)
Les structures imbriquées sont indiquées par des tabulations
b = 2
a = 12 + b
if a>b:
print(a)
else:
print(b)
a = 5
while a>0:
a = a - 1
print(a,end=" ")
for i in range(10):
print(i,end=" ")
"for" itère sur une séquence de valeurs
le corps des fonctions est indiqué par des tabulations
# arguments non typés
def max(a,b):
if a>b:
return a
else:
return b
print(max(5,4))
print(max("abc","defg"))
# typage contraint par les opérateurs
print(max("hello",34))
En plus des types de base, python peut manipuler directement les types de données structurés suivants :
Dans tous les cas, on accède aux éléments avec un index, signalé entre []
Dans le cas des listes et tuples, c'est un entier donnant la position (commence à 0)
Dans le cas des tables, l'index fait partie de la définition de la table
# tuple ... on peut mélanger les types
t=(1,3,4,"hello")
print(t[2])
(1,2,3)+(4,5)
# ne peut être modifié "immutable"
t[0] = 0
# listes ... on peut mélanger les types, mais cela n'a pas trop de sens
l=[1,2,3,4,"hello"]
print (l[1])
[1,2,3]+[4,5]
# mutable
l[4] = 6
l
valable pour tuple, liste, et chaines de caractères
# indexation négatives depuis la fin
l[-1]
# "tranches": deuxième borne non incluse
print(l[2:5])
print (l[:2])
print (l[1:-2])
print (l[3:4])
print (l[:3]+l[3:])
# avec des sauts
print(l[::2])
print(l[::-1])
La variable liste est en fait un pointeur
l = [1,2]
a = l
a[0]=8
print(a)
print(l)
Mais les sous-listes sont copiées:
l = [1,2,3]
a = l[:]
b = l[::-1]
a[0] = 8
b[0] = -2
l
#
d = {"tim":12, "bob":35, "al":27}
print(d["tim"])
Les entrées sont créées dynamiquement :
d["rt"] = 0
print(d)
Tout type immuable peut servir d'index (cela exclut donc listes et dictionnaires)
d2 = {1:3, 4:5, 1:123}
d3 = {(1,2):"?",(4,5):"."}
# seule contrainte: index immutable
() # ---> tuple
[] # ---> liste
{} # ---> dictionnaire
(2,) # tuple à un seul élément
Les types structurés sont prévus pour être l'objet d'itérations
for x in [1,2,3,4,5]:
print(x,end=" ")
print()
for x in ("a",3,-1.5):
print(x,end=" ")
for x in {1:2,10:3,100:45}:
print(x,end=" ")
d = {1:2,10:3, 100:45}
for cle in d:
print(d[cle],end=" ")
#affectation : on peut décomposer
(a,b,c) = (1,2,3)
print(a,b)
# moins utile mais possible
l = [1,2,3]
[a,b,c] = l
# seulement en python 3
a, *r = l
print(r)
# dans les itérations
l = [(1,2),(1,3),(4,5)]
for (i,j) in l:
print(i+2*j,end=" ")
# application : échange de variables
(a,b) = (b,a)
Les listes sont des objets, avec les méthodes suivantes :
l = [1,2]
l.append(10)
l.extend([3,4])
print(l+[4,5])
print(len(l))
l.append(4)
print(l)
Pour les dictionnaires:
Pour les chaines:
cf la documentation python pour les chaines
Pour tous:
Pour les dictionnaires:
d = {"tim":12, "bob":35, "al":27}
print (d)
print (d.keys())
print (d.values())
print (d.items())
print (len(d))
print (d.get('al',0))
print (d.get('d',0))
del d['al']
print (d)
print (d.get("y",0))
print (dict([("a",1),("b",3)]))
print ('x' in d)
print("hello"+"world")
print("hello world !".split())
print(" ".join(["hello","world","!"]))
# iterations abrégées
[x**2 for x in range(10)]
list1 = [1,2,3,9,100]
list2 = [x**2 for x in list1]
print(list1)
print(list2)
# itération avec l'indice:
for (i,x) in enumerate(range(100,110)):
print(i,"->",x,end=" ")
# itération abrégée des dictionnaires:
d = {1:2,3:4,5:9}
d2 = {x:d[x]**2 for x in d}
d2
les chaines sont des séquences
print("hello"[3])
print("hello"[2:5])
Il n'y a pas de type caractères: une chaine de longueur 1 est une chaine
print(type("hello"))
print(type("hello"[0]))
#plusieurs façons de décrire une chaine
"blabla"
' ou blabla '
# guillemets triples pour étaler sur plusieurs lignes
""" encore plus
de blabla"""
#Quelques méthodes
a = "Hello world !"# deux espaces entre hello et world
print(a.upper())
print (a.lower())
print (a.replace(" ","---"))
items = a.split()
print (items)
print ("---".join(items))
print (a.split("l"))
Pour gérer les fichiers, on doit gérer l'ouverture d'un canal:
f=open("monfichier.txt","w") # écriture
f.write("blabla\n")
f.write("bla")
f.close()
f=open("monfichier.txt") # lecture
print(f.read())
f.close()
f=open("monfichier.txt") # lecture
print(f.readlines())
f.close()
import math
math.exp(1)
# alternativement
from math import exp
from math import *
exp(1)
import math as fct_math
print(fct_math.exp(1))
from math import log as log2
print(log2(1))
Python peut manipuler les fonctions comme des objets, et un certain nombre de fonctions sur les fonctions sont utiles:
Exemples :
from math import sin
for x in map(sin,[0,0.5,1]):
print(x,end=" ")
def pos(x):
return x>0
for x in filter(pos,[-2,1,3,-6]):
print(x,end=" ")
Il est souvent plus facile avec ces fonctions de traiter l'argument fonction de façon "anonyme", en définissant un objet fonction jetable comme suit:
for x in filter(lambda u:u>0,[-2,1,3,-6]):
print(x,end=" ")
le format vu jusqu'ici pour les fonctions est en fait un schéma de base avec des arguments fixes:
def nom_fonction(argument1,arg2,...,argn):
On peut aussi mettre des arguments optionnels de la façon suivante :
def nom_fonction(arg1,...,argn, option1 = defaut1, option2 = defaut2, ...)
def message(texte, signature, politesse = "cordialement"):
print(texte)
print(politesse+"," )
print(signature)
message("blabla","François")
message("blabla","François Hollande",politesse = "sincerement")
# on peut avoir un nombre d'argument variable en passant un pointeur sur toute la séquence des arguments
def variable(*tous_les_args):
for x in tous_les_args:
print(x,end=" ")
variable(3,"+",4,"=",3+4)
# on peut même récupérer les options
def illisible(*args,**options):
print(args)
print(options)
illisible("euh","comment","dire",point=23,extra=2,bonus="craquage")
# on peut définir des fonctions de fonctions
def deux_f(f):
def fbis(x):
return 2*f(x)
return fbis
from math import sin
print(deux_f(sin)(0.5))
#avec une lambda
def deux_f(f):
return lambda x: 2*f(x)
print(deux_f(sin))
En python, les fonctions passent implicitement des valeurs pour les types de base (entiers, flottants, booléens), et on ne peut donc les modifier.
Les autres types, notamment les types complexes, sont passés par référence, et donc sont modifiables. Mais on peut quand même passer des valeurs, qui sont de façon transparente assignées en mémoire à une variable locale.
def test2(l):
l[0]=0
return l
l1 = [1,2,3,4]
print(test2(l1))
print(l1)
print(test2([1,2]))
Le type 'set' modélise l'équivalent d'un ensemble en mathématique: une collection d'items sans ordre spécifique.
# initialisation et ajouts
a = set([1,3,4,5,1,1,5])
b = set()
b.add(4)
b.add(6)
print(a)
print(b)
# appartenance, intersection, union
x = 3
print(x in b)
print(a,b)
print(a & b)
print(a | b)
# cardinal, inclusion , différence
print(len(a))
print(set([1,3]) <= a)
print(a - b)
print(b - a)
# suppression
a.add(4)
a.remove(4)
b.clear()
print(a,b)
a.add(4)
Python définit un ensemble d'exceptions, qui sont un type à part et qui peuvent être aussi étendues, et bien sûr levées et rattrapées.
Quelques exemples:
a = 0.
b = 15
print(b/a)
l = []
l[0]
a = 0.5
b = 15
try:
print(b/a)
except ZeroDivisionError:
print(0)
l = []
try:
print(l[0])
except IndexError:
print("?")
l = []
try:
print(l[0])
except IndexError:
print("oops")
raise
except:
print("erreur inattendue")
raise
finally:
l.append(1)
print(l)
l = []
try: # passage risqué
print(l[0])
except IndexError: # raté !
print("oops")
else: # si c'est bon on s'assure de ça (peut lever une erreur aussi)
print(1/l[0])
finally: # dans tous les cas on veut faire ça
l.append(1)
print(l)