2. Les bases de Python

2.1. Les types de données en Python

Il s’agit d’une introduction basique sur les types de données offerts par python. Cette introduction peut servir d’aide mémoire pour les utilisations les plus courantes, sachant que l’aide officiel de python est beaucoup plus complète.

2.1.1. Les nombres

C’est le type le plus simple. Il existe trois types de nombre :

  • Les entiers (type int). Par exemple a = 5, en binaire a = 0b1001, en hexadécimal a = 0x23.

  • Les nombres à virgules flottante (type float). Par exemple a = 1234.234 ou a = 6.2E-34. Les nombres sont enregistré en double précision (64 bits). La précision relative est de 52 bits, soit environ \(10^{-16}\).:

    >>> a = 3.14
    >>> a == a + 1E-15
    False
    >>> a == a + 1E-16
    True
    
  • Les nombres complexes (type complex). Il sont enregistrés sous la forme de deux nombres à virgule flottante. Par exemple a = 1 + 3J.

Les opérations sur les nombres sont les suivantes :

  • somme : +

  • produit : *

  • différence ou négation : -

  • division : /

  • division entière : //

  • modulo : % (par exemple 7%2)

  • puissance : ** (par exemple 2**10)

Pour les nombres complexes, on peut facilement accéder à la partie réelle et imaginaire de la façon suivante : :

>>> a = 1 + 3J
>>> a.imag
3.0
>>> a.real
1.0
>>> print("Norme ", sqrt(a.real**2 + a.imag**2))
Norme  3.1622776601683795

2.1.2. Les booléens et comparaison

Il existe deux valeurs : True et False (attention à la casse). Les opérations sont par ordre de priorité : not, and et or. Les comparaisons se font à l’aide des symboles <, <=, ==, > et >=. Pour savoir si deux valeurs sont différentes, on utilise !=.

Les opération and et or effectuent en fait un test conditionnel. L’instruction A and B est interprété comme B if not A else A, de même A or B équivaut à A if A else B.

Avertissement

Les symboles & et | sont des opérateurs binaires. Ils réalisent les opérations and et or sur les entiers bit par bit en binaire (par exemple 6 & 5 donne 4). Il ne faut pas les utiliser pour les opérations sur des booléens.

2.1.3. Les chaînes de caractères (string)

2.1.3.1. Création d’une chaîne de caractères

Il existe trois façons de créer une chaîne de caractère : avec des ', des " et des """. Ces caractères servent à délimiter les début et la fin du texte de la chaîne de caractère. Les guillemets simples ' et doubles " sont équivalents. On pourra choisir l’un ou l’autre. Il sera cependant judicieux, si une chaîne de caractère doit contenir un de ces guillemets, d’utiliser l’autre pour le début et la fin de la chaîne. Les trois guillemets sont eux utilisés lorsque l’on veut qu’une chaîne de caractère soit sur plusieurs lignes.

Voici quelques exemples :

>>> s = 'Pierre'
>>> s = "Aujourd'hui" #Rq : s = 'Aujourd'hui' ne va pas fonctionner
>>> s = """Aujourd'hui, le petit enfant a dit:
...  "Faisons le clown!" """

2.1.3.2. Caractères spéciaux

Les caractères spéciaux sont les caractères qui ne sont pas affichables et en tant que tel. Par exemple, il existe un caractère pour le retour à la ligne. Il est possible d’utiliser ce caractère dans une chaîne en utilisant \n. L’antislash sert ici de caractère d’échappement pour indiquer que l’on va entrer un caractère spécial. La lettre n indique ici qu’il s’agit d’un retour à la ligne.

Voici un exemple :

>>> s = "Un\nDeux"
>>> print(s)
Un
Deux
>>> len(s) # \n compte pour un caractère
7

L’antislash sert aussi à insérer un guillemet dans une chaîne : 'Aujourd\'hui'. Si on veut mettre un antislash dans un chaîne, il faut le précéder d’un autre antislash « \def ». Si on ne souhaite pas que Python interprète ces caractères spéciaux, il est possible de précéder la déclaration de la chaîne d’un r:

>>> a = r"\theta"
>>> print(a)
\theta

2.1.3.3. Manipulation des chaînes de caractères

Tout comme une liste, il est possible d’accéder à chaque caractère d’une chaîne ou à une partie d’une chaîne.

>>> s = "Pierre"
>>> s[0]
'P'
>>> s[1:3]
'ie'

La longueur de la chaîne s’obtient avec la fonction len. On peut aussi faire une boucle for sur chacun des éléments de la chaîne.

Cependant, il n’est pas possible de modifier une chaîne de caractères (l’opération s[0]='p' échoue).

La concaténation est l’opération qui consiste à créer une nouvelle chaîne en mettant à la suite deux chaînes de caractères. Elle se fait à l’aide du signe +. Par exemple :

>>> s1 = 'Un'
>>> s2 = 'Deux'
>>> s1+s2
'UnDeux'

Une autre opération est importante, il s’agit du formatage d’une chaîne de caractère. Cette opération consiste à insérer un élément variable dans une chaîne. Elle est souvent utilisée lorsque l’on veut afficher proprement un résultat. Voici une exemple :

>>> heure = 15
>>> minute = 30
>>> "Il est {0}h{1}".format(heure, minute)
'Il est 15h30'

Pour insérer un élément ou plusieurs éléments variables dans une chaîne de caractère, on crée d’abord cette chaîne en mettant à la place des ces éléments une accolade avec un numéro d’ordre {i}. En appliquant la méthode format sur cette chaîne, les accolades seront remplacées par le ième argument.

Il est possible de passer l’argument par nom dans ce cas la clé est le nom de l’argument.

>>> "Il est {heure}h{minute}".format(heure=heure, minute=minute)
'Il est 15h30'

Depuis la version 3.6 de Python, il est possible de demander à Python d’utiliser automatiquement les variables locales à l’aide du préfix f.

>>> f"Il est {heure}h{minute}"
'Il est 15h30'

Il est aussi possible de demander d’utiliser un attribut d’un objet :

>>> z = 1 + 2J
>>> print(f'Re(z) is {z.real}')
Re(z) is 1.0

En utilisant le formatage de chaîne de caractère, il est possible de spécifier en détail comment ce nombre doit s’afficher. Par exemple, si il s’agit d’un nombre à virgule flottante, combien de décimales faut-il afficher, faut il utiliser la notation scientifique, etc. Pour cela, on rajoute à l’intérieur des accolades un code particulier. Ce code est précédé du signe “:”.

Voici quelques exemples :

>>> from math import pi
>>> '{0:.5f}'.format(pi)
'3.14159'
>>> c = 299792458. # Vitesse de la lumière en m/s
>>> 'c = {0:.3e} m/s'.format(c)
'c = 2.998e+08 m/s'

Le “f” indique que l’on veut une notation a virgule fixe, le “e” une notation scientifique. Le chiffre que l’on indique après le “.” donne le nombre de chiffre après la virgule que l’on souhaite.

Note

L’aide en ligne de Python fournit d’autres exemples et des détails.

Il existe aussi une façon plus élémentaire de formater des chaîne de caractères avec Python et qui est obsolète (mais que l’on peut rencontrer). Pour formater le nombre pi, cette méthode écrira dans ce cas '%.6f'%pi.

2.1.4. Les listes (list)

  • On peut créer et remplir une liste de plusieurs manières :

    >>> l = [1, 2, 3, 4]
    >>> l = [] # liste vide
    >>> l.append(3)
    >>> l
    [3]
    >>> l.append(4)
    >>> l
    [3, 4]
    >>> l.insert(0,3.24+1j)
    >>> l
    [(3.24+1j), 3, 4]
    >>> len(l) # Longueur de la liste
    3
    
  • On peut changer un élément de la liste :

    >>> l[2] = 23
    
  • On peut créer une nouvelle liste à partir d’une liste à l’aide de la commande list :

    >>> l = [2, 3]
    >>> m = list(l)
    >>> l.append(45)
    >>> l==m
    False
    
  • La commande range(n) peut être utilisée pour créer la liste [0,1,2, .., n-2, n-1]. Cette liste commence à 0 et contient n nombres (elle s’arrête donc à n-1).

    >>> list(range(10))
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    Techniquement, depuis la version 3 de Python, range ne renvoie plus une liste, mais un générateur. Ce générateur produit la suite de chiffre qui est ensuite utilisée par le constructeur de la liste.

  • Une liste peut contenir des éléments de types différents.

  • Souvent on a besoin de créer une liste dans une boucle. Par exemple la liste des n premiers nombres au carré peut se calculer de la façon suivante

    l = []
    for i in range(n):
        l.append(i**2)
    

    Il existe cependant une façon plus directe de faire, en utilisant les list comprehension

    l = [i**2 for i in range(n)]
    

    Cette syntaxe se lit directement en français : calcule la liste des i au carré pour i allant de 0 à n. Il est aussi possible de rajouter une condition ( pour i allant de 0 à n si i est pair )

    l = [i**2 for i in range(n) if i%2==0]

  • Si on souhaite appliquer une fonction à tous les éléments d’une liste pour recréer une liste, il est possible d’utiliser la fonction map

    def mafonction(i):
        if i%2==0:
            return i**2
        else:
            return i**2-1
    
    map(mafonction, range(5)) # [0,0,4,8,16]
    
  • On peut parcourir une liste de plusieurs façons

    l = [1, 3, 5, "Pierre"]
    for elm in l:
        print(elm)
    
    for i,elm in enumerate(l):
        print(elm, " est l'element numero ",i," de la liste)
    

Avec les listes, nous avons donc introduit la structure de contrôle for. Celle ci fonctionne sur tout type de séquence (par exemple les chaînes de caractères). Par cette syntaxe, elle est assez différente de ce qui existe dans d’autres langage informatique.

Note

Nous avons vu qu’il était possible de rajouter des éléments dans une liste. Pour cela, nous avons utilisé la syntaxe l.append(elm). Il s’agit ici de programmation orientée objet. Il est utile de savoir reconnaître la syntaxe : la liste est suivi du nom de la fonction que l’on applique (on appelle cette fonction une méthode) avec ses arguments. Ici, donc, append rajoute elm à la liste l.

Notons de plus, que l’objet l est modifié. La variable l désigne toujours la même liste, mais cette liste est modifiée (si on ajoute une page à un classeur, il s’agit toujours du même classeur). Ce comportement est différent de celui que nous avons vu dans la paragraphe précédent avec la méthode format sur les chaînes de caractères. En effet dans ce cas, on crée un nouvelle chaîne de caractère qui est renvoyé par la méthode - la chaîne initiale n’étant pas modifiée. La méthode append appliquée à une liste, modifie la liste, mais ne renvoie rien.

  • Pour trier une liste, on peut soit utiliser la méthode .sort qui modifie la liste soit la fonction sorted qui renvoie une nouvelle liste triée. Il est possible d’ajouter comme argument optionnel une fonction qui donne l’ordre. Par défaut, python utilise la fonction cmp (ordre croissant pour les nombre et alphabétique pour le chaîne)

    l = ['Pour', 'trier', 'une', 'liste', 'on', 'peut']
    print(sorted(l))
    
    def compare(a,b):
        u""" Ordre alphabétique inversé sans tenir compte
        de la casse"""
        return cmp(b.lower(), a.lower())
    
    sorted(l, compare)
    
  • Fonction zip : lorsque l’on veut parcourir deux listes en même temps, il est possible d’utiliser la fonction zip qui crée alors une liste de n-uplets à partir de n listes. :

    >>> liste_nom = ['Martin', 'Lefevre', 'Dubois', 'Durand']
    >>> liste_prenom = ['Emma', 'Nathan', 'Lola', 'Lucas']
    
    >>> for nom, prenom in zip(liste_nom, liste_prenom):
    ...     print(prenom, nom)
    Emma Martin
    Nathan Lefevre
    Lola Dubois
    Lucas Durand
    

2.1.5. Les n-uplets (tuple)

Les n-uplets sont utilisés lorsque l’on veut regrouper des valeurs ensembles. On utilise une liste lorsque l’on a une longue séquence (dont la longueur est souvent variable) et un n-uplet pour regrouper quelques éléments. Par exemple :

>>> personne = ('Jean', 'Dupont', '13 juillet 1973', 38)
>>> print('Nom :',personne[1])
Nom : Dupont

Les n-uplets sont utilisés lorsqu’une fonction renvoie plusieurs éléments :

>>> def fonction(x):
...    return x**2,x**3
>>> a = fonction(4)
>>> a
(16, 64)
>>> a,b = fonction(4)

2.1.6. Les dictionnaires (dictionnary)

Contrairement aux listes ou aux n-uplets qui sont indexés par des nombres (l[2]), un dictionnaire est indexé par une clé. En général, la clé est une chaîne de caractère ou un nombre. On peut reprendre l’exemple précédent :

>>> personne = {"Prenom":"Jean", "Nom":"Dupont",
...   "date_naissance":"13 juillet 1973", "Age":38}
>>> print("Nom :",personne['Nom'])
Nom : Dupont

Tout comme pour une liste, on peut ajouter ou enlever des éléments à un dictionnaire :

>>> personne['telephone'] = "02 99 79 24 58"
>>> del personne['Age']

Il est possible de parcourir et accéder à un dictionnaire de plusieurs façons :

>>> releve_note = {'Jacques':15, 'Jean':16, 'Pierre':14}

# Par clef
>>> for nom in sorted(releve_note.keys()):
...    print('La note de {0} est {1}'.format(nom, releve_note[nom]))
La note de Jacques est 15
La note de Jean est 16
La note de Pierre est 14

# Par valeurs
>>> notes = releve_note.values()
>>> print("Moyenne", sum(notes)/len(notes))
Moyenne 15.0
# Par clef et valeur
>>> for cle, val in releve_note.items():
...    print('La note de {0} est {1}'.format(cle, val))
La note de Pierre est 14
La note de Jean est 16
La note de Jacques est 15

La clé d’un dictionnaire doit être un objet non modifiable, c’est à dire un nombre, une chaîne de caractère ou un tuple contenant uniquement des objets non modifiables.

2.1.7. Les ensembles (set)

Utilisé pour représenter un ensemble non-ordonné d’éléments qui sont tous différents. Par exemple :

>>> a = set([1,2,3])
>>> b = set([3,5,6])
>>> c = a | b # union
>>> d = a & b # intersection

Un exemple d’application :

>>> mdp = raw_input('Entrez un mot de passe contenant \
...                        au moins un signe de ponctuation')
>>> ponctuation = set("?,.;:!")
>>> if (ponctuation & mdp == set()):
...    print("Le mot de passe ne contient pas de signe de ponctuation")

Les ensembles sont très pratiques lorsque l’on veut supprimer des doublons.

Exercice

Récupérez un texte quelconque en chinois (par exemple https://www.gutenberg.org/files/24051/24051-0.txt). Combien d’idéogrammes chinois différents sont utilisés dans ce texte?

2.2. Expressions en Python

Dans un langage de programmation, on distingue les expressions des commandes. Les expressions vont être évaluer par l’interpréteur pour renvoyer un objet.

Nous n’allons pas faire une description exhaustive de toutes les possibilités.

  • Les parenthèses peuvent avoir plusieurs sens :

    >>> sin(1 + 2) # appel d'une fonction
    0.1411200080598672
    >>> (1 + 2J)*3 # parenthèse logique
    (3+6j)
    >>> (1, 2J)*3 # n-uplet
    (1, 2j, 1, 2j, 1, 2j)
    
  • Nous vous rappelons la syntaxe dite de list comprehension :

    >>> [i**2 for i in range(10)]
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> [i//3 for i in range(10) if i%2==0]
    [0, 0, 1, 2, 2]
    

    De même il existe des comprehension pour les dictionnaires et les ensembles :

    >>> s = {chr(97+i) for i in range(10) if i%2==0} # ensemble
    >>> s == {'i', 'c', 'g', 'e', 'a'}
    True
    >>> d = {chr(97+i):i for i in range(10) if i%2==0} # dictionnaire
    >>> d == {'e': 4, 'c': 2, 'g': 6, 'i': 8, 'a': 0}
    True
    
  • Il est possible d’utiliser une condition dans une expression sous la forme intuitive renvoie A si B sinon C. Ce type d’expression permet d’éviter de passer par des variables intermédiaires.

    sqrt(x) if x>=0 else sqrt(-x)*1J
    print("Bonjour" if lang=='fr' else 'Hello')
    

2.3. Créer des fonctions

2.3.1. Arguments d’une fonction

Voici un exemple général de la définition d’une fonction :

>>> def f(a, b, c=1, d=2, *args, **kwd):
...     print(a, b, c, d)
...     print(args)
...     print(kwd)

Cette fonction possède deux arguments obligatoires, deux arguments optionnels. Les variables args et kwd vont contenir les arguments supplémentaires (sans et avec mots-clé - keyword).

Lorsque l’on appelle une fonction, les arguments peuvent être passés anonymement (par ordre) ou avec un nom (keyword argument, nom=valeur). Il faut mettre d’abord les arguments anonymes puis les autres. Il n’y a pas de lien entre le fait qu’un argument ait une valeur par défaut et le fait qu’il soit utilisé avec son nom. Lorsque les arguments sont passés avec leur nom, l’ordre est indifférent :

>>> f(1, 2, 4)
1 2 4 2
()
{}
>>> f(b=2 , a=2)
2 2 1 2
()
{}

Les arguments en trop sont envoyés dans args ou kwd :

>>> f(1, 2, 3, 4, 5, 6)
1 2 3 4
(5, 6)
{}
>>> f(1, 2, 3, e=4)
1 2 3 2
()
{'e': 4}
>>> f(1, 2, 3, 4, 5, 6, e=7)
1 2 3 4
(5, 6)
{'e': 7}

Enfin, il est possible de séparer un groupe d’arguments à partir d’un itérable (list, tuple, ..) (séparation anonyme) ou à partir d’un dictionnaire (séparation avec mots-clés):

>>> liste = list(range(1,3))
>>> dct = {'d':3, 'e':4}
>>> f(0, *liste, **dct)
... # equivaut à f(0, 1, 2, d=3, e=4)
0 1 2 3
()
{'e': 4}

On remarquera que les variables args et kwd à l’intérieur de la fonction f sont différentes de celles que l’on a séparées (liste et dct dans cet exemple).

Il est de toute façon possible de séparer plusieurs listes ou dictionnaires :

>>> print(*range(3), *range(3))
0 1 2 0 1 2

Quelques remarques :

  • Il ne faut par hésiter à utiliser des arguments par défaut (et c’est mieux que des variables globales)

  • Lorsque l’on appelle une fonction, il ne faut pas hésiter à nommer les arguments, même si c’est inutile et que c’est plus long à taper. Comparez

    >>> scope.configure_channel(1, 0.01, 0.03, 50)
    >>> scope.configure_channel(channel_name=1, scale=0.01, offset=0.03, impedance=50)
    

2.3.2. Variable locale/globale

Les variables que l’on crée dans une fonction sont locales, c’est à dire indépendante d’une variable extérieure à la fonction et qui porte le même nom.

>>> def f():
...     x = 2
>>> x = 3
>>> f()
>>> x
3

A l’intérieur d’une fonction, une variable est soit locale soit globale (elle ne peut pas changer en cours).

2.4. Les boucles

Il existe des boucle for et des boucle while.

Pour sortir d’une boucle on peut utiliser l’instruction break, pour passer à l’itération suivante l’instruction continue. Si une boucle se finit normalement (sans break), il est alors possible d’exécuter un bloc d’instruction dans un else. Voici un examole:

def affiche_si_premier(n):
    i=2
    while i**2<=n:
        if n%i==0:
            print("{} n'est pas premier".format(n))
            break
        i = i+1
    else:
        print('{} est premier'.format(n))

Remarquons que en Python il est possible de quitter une fonction à n’importe quel moment à l’aide de l’instruction return. Lorsque dans une boucle on connaît le résultat de la fonction, il est alors préférable de quitter celle ci immédiatement:

def est_premier(n):
    i=2
    while i**2<=n:
        if n%i==0:
            return False
        i = i+1
    return True

On peut parcourir une liste directement, sans passer par les indices :

panier = ['carottes', 'courgettes', 'tomates']
quantite = [1, 3, 2]

for legume in panier :
    print(legume)

# Si on souhaite l'indice:
for i, legumes in enumerate(panier):
    print(f'{i}: {legume}')

# Parcourir deux listes en même temps
for poids, legumes in zip(quantite, panier):
    print(f'{poids}kg de {legume}')
carottes
courgettes
tomates
0: tomates
1: tomates
2: tomates
1kg de tomates
3kg de tomates
2kg de tomates