11. Les séquences immutables#

Nous abordons maintenant des structures de données permettant de stocker plusieurs valeurs à la fois.

11.1. N-uplets - tuple#

Un n-uplet (ou tuple en anglais) est une séquence de valeurs regroupées en un tout. On utilise les paranthèses pour représenter les n-uplets en Python, les valeurs sont séparées par des virgules. Par exemple le n-uplet (1, 4, “a”) contient trois valeurs 1, 4 et “a”. On remarque ici qu’un n-uplet peut contenir des valeurs de types différents. Une variable peut être liée à un n-uplet.

a = (1, 4, 'a')
print(a)

On peut également crée un n-uplet avec un seul élément. Pour ne pas le confondre avec un entier paranthésé, Python impose qu’on ajoute une virgule.

a = (1, )
print(a)

11.1.1. Accès aux valeurs#

Les valeurs contenues dans le n-uplet sont indicées (en commençant à 0). Il est possible d’accéder aux valeurs contenu dans un n-uplet avec la notation crochet : a[indice].

a = (1, 4, 'a')
print(a[0])
print(a[1])
print(a[2])

Il faut veiller à ne pas tenter d’accéder à valeur en utilisant un indice trop grand. Dans l’exemple suivant, le n-uplet a possède 3 valeurs. Il est donc impossible d’accéder à la valeur d’indice 3 (on numérote en débutant à 0, on compte le nombre de décalage depuis le premier élément). Python génère une erreur dont le message est explicite : tuple index out of range.

a = (1, 4, 'a')
a[3]

Il n’est pas possible de modifier les valeurs contenues dans un n-uplet. C’est un type dit immutable. L’exemple suivant entraine une erreur dont le message est là encore très clair : ‘tuple’ object does not support item assignment.

a = (1, 4, 'a')
a[0] = 100

On peut également accéder aux valeurs en partant de la fin du n-uplet. Pour cela on utilise de indice négatif en partant de -1 pour le dernier.

a = (1, 4, 'a')
print(a[-1])
print(a[-2])
print(a[-3])

Là encore, il faut être vigilent et ne pas dépasser les limites de notre séquence. Le code suivant génère une erreur.

a = (1, 4, 'a')
a[-4]

11.1.2. Longueur#

Pyhton dispose d’une fonction len qui permet de connaître le nombre de valeurs contenues dans un n-uplet.

a = (1, 4, 'a')
longueur = len(a)
print(longueur)

La fonction len permet de déterminer les limites d’un n-uplet. Pour notre n-uplet a,

  • les indices sont compris par ordre croissant de 0 à len(a) -1 ;

  • ou par ordre décroissant de -1 à -len(a).

11.1.3. Concaténation#

Il est impossible de modifier le contenu d’un n-uplet mais il est possible de concaténer des n-uplets ensemble pour en créer un nouveau avec l’opérateur +. Dans l’exemple suivant on utilise la fonction id qui permet d’obtenir l’identité d’un objet (le plus souvent, son adresse en mémoire) afin de montrer que le n-uplet résultant de la concaténation est bien nouveau.

a = (1,2)
b = (3,4)
c = a+b
print(c)
print(id(a), id(b), id(c))

11.1.4. Comparaison#

Python permet de comparer des n-uplets afin de déterminer s’ils sont égaux où non.

print( (1,2) == (1,2) )
print( (1,2) == (1,2,3) )
print( (1,2) == (2,1) )

Il est possible de tester si un valeur est contenue dans un n-uplet avec l’opération in. Le résultat sera une valeur booléenne.

print(1 in (1,2,3,4))
print(0 in (1,2,3,4))

Les n-uplets servent à regrouper des valeurs ensembles et à les manipuler comme un tout. Une fonction qui ne peut renvoyer qu’une seule valeur avec le mot return peut utiliser un n-uplet pour regrouper plusieurs valeurs en un tout.

11.2. Exercices#

Question : Écrivez une fonction sort_two_numbers qui prend en paramètre deux nombres et renvoie une séquence contenant les deux nombres par ordre croissant.

def sort_two_numbers(a, b):
    pass

Voici une correction possible.

def sort_two_numbers(a, b):
    if a < b:
        return (a,b)
    else:
        return (b,a)

Question : Faites une fonction similaire sort_three_numbers mais pour trois nombres.

Indice : Vous pouvez utiliser la fonction sort_two_numbers déjà créée. Rappelez-vous qu’un tuple avec un seul élément x se note (x, ).

Voici une correction possible.

def sort_three_numbers(a, b, c):
    t = sort_two_numbers(a, b)
    if c < t[0] :
        return (c, ) + t
    elif t[1] < c :
        return t + (c, )
    else :
        return (a, c, b)

11.3. Chaînes de caractères - str#

Nous avons déjà manipuler les chaînes de caractères sans vraiment expliquer leur fonctionnement. En Python, une chaîne de caractètes est une séquence et à ce titre est très proche d’un n-uplet. Ainsi, tout comme un n-uplet, il est possible d’accéder à un caractère en particulier de la chaîne à partir de son indice.

chaine = "abcd"
print(chaine[0])
print(chaine[3])

Tout comme un n-uplet il est impossible de modifier une chaine : ‘str’ object does not support item assignment.

chaine = "abcd"
chaine[0] = 'e'

Tout comme un n-uplet il est possible de connaître la longueur d’une chaine avec la fonction len, de concaténer des chaînes ensembles, de déterminer si deux chaînes sont égales ou de tester si une sous-chaine est contenue.

print( len("Cette chaine est trop longue est " + "concatenée a une autre") )
print( "oui" == "oui" )
print( "oui" == "Oui" )   # sensible a la casse...
print( "Supélec" in "CentraleSupélec" )

Dernière chose, un caractère seul est également une chaine. Lorsque l’on accède à un élément de la chaine à partir d’un indice, on récupère une chaine de caractères d’un unique caractère.

11.4. Exercices#

Question : Écrivez une fonction add_point qui prend en paramètre un chaine de caractères et ajoute un point à la fin si cette chaine n’est contient pas déjà un.

Indice : Rappelez vous des indices négatifs.

def add_point(c):
    pass

print(add_point("Une chaine."))
print(add_point("Une chaine"))
None
None

Voici une correction possible.

def add_point(c):
    if c[-1] == ".":
        return c
    else :
        return c+"."

Question : Notre fonction n’est pas idéale car elle ajoute un point pour des chaines qui se terminent par des point d’exclamation ou d’intérogation. Adaptez votre fonction pour que cela ne soit plus le cas.

Indice : Utilisez l’oppérateur in.

def add_point(c):
    pass

print(add_point("Une chaine."))
print(add_point("Une chaine"))
print(add_point("Une chaine!"))
None
None
None

Voici une correction possible.

def add_point(c):
    if c[-1] in ".!?":
        return c
    else :
        return c+"."

11.5. Séquence de nombres - range#

Python permet de générer automatiquement des suites de nombres entiers. Pour cela, on utilise la fonction range. Cette fonction crée un générateur, un objet qui permet de générer une suite. En fonction du nombre de paramètres donnés à range son fonctionnement varie. Avec une seule valeur v, range crée une suite d’entiers compris entre 0 inclu et v exclu.

Dans l’exemple suivant on crée un générateur puis on l’affiche. On voit que l’on a pas fabriqué de suite de nombre, uniquement un générateur. Pour obtenir une suite de nombre, on peut convertir le générateur en un tuple.

r = range(10)
print( r )

t = tuple(r)
print(t)

Comme pour les séquences, il est possible d’utiliser les opérations que nous avons vu précédement comme la notation [indice] ou la longueur len.

r = range(10)
print( r[5] )
print( len(r) )

Python propose de construire des séquences de nombre entre deux valeurs et également entre deux valeurs avec un pas.

print( tuple( range(1,10) ))
print( tuple( range(1,10,2) ))

Ce dernière notation nous permet de faire des séquences de nombres décroissants.

print( tuple( range(10,1,-1) ))

11.6. Le slicing#

Une opération particulièrement importante avec les séquences est le slicing. C’est la possibilité d’extraire une sous-séquence. L’exemple suivant extrait la sous-chaine contenant les 10 premiers caractères d’une chaine. On indique l’indice du premier et dernier élément à extraire séparé par :. Tout comme pour range, le premier est inclu alors que le dernier est exclu.

c="abcdefghijklmnopqrstuvwxyz"
print(c[0:10])

On peut également indiquer que l’on part du début pour aller jusqu’à un certain indice, ou bien d’un certain indice pour aller jusqu’à la fin.

c="abcdefghijklmnopqrstuvwxyz"
print(c[:2])
print(c[2:])

Il est possible de préciser un pas. Ici, on extrait les valeurs depuis la première et jusqu’à la dernière mais on n’en conserve qu’une sur deux.

c="abcdefghijklmnopqrstuvwxyz"
print(c[::2])

Utiliser des indices négatifs indique que l’on débute depuis la fin de la liste. L’indice -1 représente donc le dernier élément de notre séquence, -2 l’avant dernier, …

c="abcdefghijklmnopqrstuvwxyz"
print(c[-5:])

Les séquences que nous manipulons sont immutables. Cela signifie que le résultat d’un slicing est bien une nouvelle séquence. L’opération de slicing est un moyen simple d’obtenir la copie d’une séquence.

11.7. Séquences et fonctions, clarifications#

Voici des éléments supplémentaires et des clarifications sur le comportement des fonctions.