Initiation à Python

Écriture de scripts

Un script Python est un fichier texte enregistré avec l'extension .py que l'on peut créer par exemple avec un simple éditeur de texte comme le bloc note de Windows. Mais il est plus pratique d'utiliser un éditeur adapté pour l'écriture de scripts, comme Notepad++, PyScripter, Geany ou Bluefish.

Le langage Python est multi-paradigme, c'est-à-dire qu'il admet plusieurs manières différentes de traiter les problèmes. Les structures de contrôle (branchement conditionnel, boucles) permettent une programmation impérative en enchainant des instructions. La définition de fonctions réduit les répétitions dans le code et ouvre le champ à la programmation fonctionnelle. Mais surtout Python offre un cadre clair pour découvrir la programmation orientée objet : la structure des objets modélisés (atome, opération comptable, grammaire linguistique, variété algébrique, génôme…) est transcrite directement dans le code, avec les propriétés, relations et opérations associées.

Toutes ces approches ne sont pas incompatibles et un script Python peut très bien commencer par une déclaration d'objets, utiliser le lambda-calcul et se terminer par un algorithme impératif.

En-tête

Un script Python n'a pas besoin d'une en-tête a priori, mais il est utile d'indiquer sur la première ligne le chemin d'accès vers l'interpréteur lorsqu'il est connu, précédé du shebang #!. On peut même préciser la version de Python avec laquelle le script doit être exécuté.

Sur la deuxième ligne, il est important de spécifier la norme de codage utilisée pour la saisie du programme, surtout si on y emploie des caractères accentués, d'autres signes diacritiques ou des caractères plus exotiques encore (Unicode), selon les cas ISO-8859-1 ou UTF-8. Les deux premières lignes du programme pourront donc apparaitre comme suit :

#! /usr/bin/env python3
# -*- coding: ISO-8859-1 -*-

Structures de contrôle

Contrairement à beaucoup de langages de programmation, Python ne délimite pas l'intérieur d'une boucle ou d'un branchement conditionnel à l'aide de commandes comme begin et end, mais par une indentation du code (en général avec quatre espaces). Les deux-points et l'indentation sont indispensables.

# Exemple d'utilisation d'un branchement conditionnel
from time import strftime
h = int(strftime('%H'))  # formatage de l'heure courante
if h < 18:
    print("Bonjour !")
elif h < 22:
    print("Bonsoir !")
else:
    print("Bonne nuit !")

# Exemple d'utilisation d'une boucle itérative
for k in range(10, 0, -1):  # intervalle parcouru en décroissant
    print("C'est dans %d an%s, je m'en irai," % (k, (k>1)*'s'))
    print("j'entends le loup et le renard danser...")
else:
    print("C'est cette année que je m'en vais...")

# Exemple d'utilisation d'une boucle conditionnelle
from random import randint
while randint(1, 6) < 6:
    print("Raté.")
else:
    print("Six !")

La boucle for peut parcourir n'importe quel itérateur, entre autres les listes, uplets, chaines de caractères, dictionnaires, ensembles.

Les commandes elif et else sont facultatives. En sortie de boucle, elles sont même a priori inutiles mais peuvent différencier la sortie normale (fin de parcours de l'itérateur ou négation de la condition de boucle) d'une sortie sur interruption ou exception.

En effet, les boucles for et while peuvent être interrompues par l'instruction break.

# Détection de la présence d'une lettre en 6 exemplaires
occurrences = {}
for c in "Supercalifragilisticexpialidocious":
    if occurrences.has_key(c):
        occurrences[c] += 1
        if occurrences[c] == 6:
            print("La lettre %s apparait au moins 6 fois." % c)
            break  # fin de la recherche, sortie de boucle
        # pas de else pour ce branchement
    else:   # nouvelle lettre
        occurrences[c] = 1
else:       # fin du mot
    print("Pas de lettre en 6 exemplaires ou plus.")

L'instruction continue à l'intérieur d'une boucle permet de passer directement à l'étape suivante de la boucle. L'instruction pass correspond à une action vide mais peut être utile lorsque la syntaxe exige au moins une instruction.

Exercice
  1. Calculer le premier nombre de la suite de Fibonacci supérieur à un milliard.
  2. Calculer le nombre de nombres premiers à cinq chiffres.
  3. Appliquer la méthode d'Euler à la résolution de l'équation différentielle y′ = y2 + 1 avec y(0) = 0 en cent pas entre 0 et π/4 et évaluer l'écart avec la valeur théorique.

Fonctions

Une fonction est une suite d'instructions qui peuvent être appliquées à plusieurs objets donnés en paramètres et renvoyer éventuellement un résultat. Les noms des variables désignant les paramètres peuvent coïncider avec des noms de variables extérieures à la fonction sans impact sur ces dernières. Mais lorsque des objets modifiables (listes, objets…) sont passés en paramètres, ils sont susceptibles d'être modifiés lors de l'exécution de la fonction, par effet de bord.

Il existe plusieurs manières de saisir une fonction. Les différentes formes sont plus ou moins adaptées selon les contextes.

# À la manière du lambda-calcul
add = lambda x: lambda y: x+y
f = add(4)
print(f(3)) # affiche : 7

# Définition de fonction
def polynome(coeffs):
    def fpolynome(x):
        resultat = 0
        puissance = 1
        for c in coeffs:
             resultat += c*puissance
             puissance *= x
        return resultat
    return fpolynome

cube = polynome([0, 0, 0, 1])
print(cube(2))  # affiche : 8

def toutincremente(liste_nbs):
    for i in range(len(liste_nbs)):
         liste_nbs[i] += 1

print(toutincremente([1, 2, 4]))  # affiche : None
# En effet, la fonction toutincremente ne renvoie rien.
liste = [1, 2, 4]
toutincremente(liste)
print(liste)  # affiche : [2, 3, 5]

La deuxième forme de définition des fonctions permet de donner des valeurs par défaut aux paramètres, à l'aide de l'opérateur =. Il est même possible d'indiquer par une annotation la signification de chaque argument et du résultat.

def segmente(chaine: "mot à découper", taille: "longueur des segments" = 1, reste: "il y en a un peu plus, je vous le mets ?" = True) -> "liste des segments":
    resultat = []
    i = 1
    segment = []
    for c in chaine:
        segment.append(c)
        if i == taille:
            resultat.append(''.join(segment))
            segment = []
            i = 1
        else:
            i += 1
    if segment and reste:
        resultat.append(''.join(segment))
    return resultat

segmente('la valse à mille temps', 3)
# affiche ['la ', 'val', 'se ', 'à m', 'ill', 'e t', 'emp', 's']
Exercice
Construire les fonctions suivantes :
pgcd(a, b)
calcul du PGCD de deux entiers (éventuellement étendre à une famille finie d'entiers
ppcm(a, b)
calcul du PPCM (on peut recourir à la fonction précédente mais attention aux erreurs avec 0)
est_palindrome(texte)
vrai si le texte (sans signes diacritiques) est un palindrome (sans tenir compte des espaces ni des majuscules), faux sinon
syracuse(n)
suite de Syracuse associée à l'entier n
univoyelles(texte)
liste des mots du texte ne comportant qu'une seule voyelle
GramSchmidt(u, v, w)
base orthonormale de R3 obtenue par le procédé d'orthornormalisation de Gram-Schmidt à partir de trois vecteurs listes de 3 éléments chacune.

Plutôt que de renvoyer une liste, une fonction peut constituer un générateur dont les valeurs sont calculées successivement à chaque appel.


def premiers():
    yield 2;
    liste = []
    n = 3
    while True:
        for p in liste:
            if n % p == 0:
                break
            elif p*p > n:
                yield n
                liste.append(n)
                break
        else:
            yield n
        liste.append(n)
        n += 2

def premier_suivant(seuil):
    for p in premiers():
        if p > seuil:
            return p

Classes et objets

Les classes permettent de définir des types complexes en intégrant les opérations usuelles associées. Chaque fonction définie à l'intérieur de la classe est à utiliser comme une méthode (c'est le sens du mot-clé self). Certaines de ces fonctions (nommées avec deux caractères souligné de chaque côté) servent pour les opérateurs et les fonctions globales.

class Polynome:
    """Une classe pour les polynômes"""
    def __init__(self, coeffs):  # constructeur
        if coeffs:
            self.coeffs = list(coeffs)
        else:
            self.coeffs = []
        k = len(self.coeffs)
        for c in self.coeffs[::-1]:
            k -= 1
            if c:
                self.deg = k
                break
            else:
                self.deg = float(-inf)
    def ev(self, x):
        """Évaluation en un scalaire."""
        resultat = 0
        puissance = 1
        for c in self.coeffs:
            resultat += c*puissance
            puissance *= x
        return resultat
    def __repr__(self, indeterminee = 'X'):  # pour fonction print
        return ''.join([
            (' + ' if c > 0 else ' - ')
            + (str(abs(c)) if k == 0
                else (('' if abs(c) == 1 else str(abs(c)))
                    + indeterminee + ('^' + str(k) if k > 1 else '')))
            for (k,c) in list(zip(range(self.deg+1),self.coeffs))[::-1]
            if c]).strip(' +')
    def __add__(self, other):  # pour opérateur +
        coeffs = [a+b for (a, b) in zip(self.coeffs, other.coeffs)]
        if self.deg > other.deg:
            coeffs += self.coeffs[len(other.coeffs)-1:self.deg+2]
        else:
            coeffs += other.coeffs[len(self.coeffs)-1:other.deg+2]
        return Polynome(coeffs)
    def derive(self):
        """Construction du polynome dérivé."""
        k = 0
        coeffs = []
        for c in self.coeffs[1:]:
            k += 1
            coeffs.append(k*c)
        return Polynome(coeffs)

P = Polynome([4, 3, -0.5, 0, 2])
print(P.derive())  # affiche 8X^3 - X + 3
Exercice
  1. Définir la fonction __mul__ de multiplication de polynômes, permettant l'usage de l'opérateur *.
  2. Définir une classe Atome avec les champs symbole, Z (numéro atomique), valence, puis construire quelques atomes.
  3. Définir une classe Repere avec des champs précisant une position et une orientation dans l'espace, puis définir les fonctions de mouvement avant, lacet, roulis et tangage.

Exceptions

Les exceptions permettent de gérer les erreurs d'exécution (mais pas les erreurs de syntaxe !) en renvoyant un code d'erreur qui pourra être traité à un niveau supérieur.

import math
def sinc(x):
    try:
        return math.sin(x)/x
    except ZeroDivisionError:
        return 1

def moyenne(liste):
    if len(liste)==0:
        raise NameError('Liste vide')
    return sum(liste)/len(liste)