Licence CC BY-NC-ND, Thierry Parmentelat & Arnaud Legout

from IPython.display import HTML
HTML(filename="_static/style.html")

séquences & chaines#

c’est quoi une séquence ?#

  • suite finie et ordonnée d’objets

  • du coup indexable, on peut écrire S[n]

    • en Python, les indices commencent à 0

  • peuvent contenir des duplications

les séquences dans le langage Python (entre autres):

  • mutable

    • list, bytearray

  • immutable

    • str, bytes, tuple, range

les chaines (str) sont des séquences

nous allons voir pour commencer des choses valables sur toutes les séquences, et donc en particulier sur les chaines de caractères, puisque les chaines sont des séquences

fonctions sur toutes les séquences#

indexation, longueur#

  • S[i]

    • retourne l’élément d’indice i

  • len(S)

    • donne la taille en nombre d’éléments

les séquences sont des itérables#

un itérable en Python, c’est un objet sur lequel on peut faire un for

for item in S:
    # do something with item

concaténation, comparaisons#

  • S + T

  • retourne une nouvelle séquence qui est la concaténation de S et T

  • S*n ou n*S

    • retourne une nouvelle séquence qui est la concaténation de n shallow copies de S

  • sum(S) (resp. min(S) et max(S))

    • retourne la somme des éléments de S (resp. le plus petit, le plus grand)

    en fait sur tous les itérables

    en fait les fonctions sum, min et max s’appliquent à n’importe quel itérable
    toutes les séquences sont des itérables, mais tous les itérables ne sont pas des séquences
    la famille des itérables est donc plus large que les séquences

appartenance, recherches#

  • x in S; selon les types:

    • True si un élément de S est égal à x
      (e.g. S est une list)

    • True si S contient x
      (e.g. S est une str)

  • diverses méthodes (voir docs):

    • S.count(a)

      • retourne le nombre d’occurrences de a dans S

    • S.index(a), S.find(a)
      retourne l’indice de la première occurrence de a dans S

slicing#

  • S[i:j] retourne

    • une nouvelle séquence de même type

    • contenant tous les éléments de l’indice i à l’indice j-1

  • S[i:j:k] retourne

    • une nouvelle séquence de même type

    • prenant tous les éléments de l’indice i à l’indice j-1,

    • par sauts de k éléments

  • les indices i, j et k peuvent être négatifs

voyons des exemples de tout ceci

slicing et numpy

la notion de slicing est très massivement utilisée dans toutes les librairies numériques, notamment en numpy

indices omis et négatifs#

  • omettre les bornes

  • compter depuis la fin, avec un indice négatif

_images/egg-bacon.svg
s = "egg, bacon"
s[0:3]
'egg'
# si on omet une borne
# ce sera le début ..
s[:3]
'egg'
# ... ou la fin:
s[5:]
'bacon'
# les indices peuvent être négatifs
s[-3:10]
'con'
# tout entier: une shallow-copy
s[:]
'egg, bacon'

les bornes#

_images/egg-bacon-bornes.svg

La convention est choisie pour pouvoir facilement “encastrer” les slices:

s[0:3]
'egg'
s[3:6]
', b'
s[6:]
'acon'
s[0:3] + s[3:6] + s[6:] == s
True

le pas#

_images/egg-bacon.svg
  • on peut préciser un pas

  • qui peut aussi être négatif

  • ou omis (défaut 1)

s[0:10:2]
'eg ao'
s[::2]
'eg ao'
s[:8:3]
'e,a'
s[-2::-3]
'obg'

pas d’exception#

_images/egg-bacon.svg

les slices ont un comportement plus permissif que l’indexation

# Si j'essaie d'utiliser un index inexistant
try: s[100]
except Exception as e: print("OOPS", e)
OOPS string index out of range
# par contre avec un slice, pas de souci
s[5:100]
'bacon'
# vraiment..
s[100:200]
''

exemples#

_images/egg-bacon.svg
s[-1]
'n'
s[-3:-1]
'co'
s[:-3]
'egg, ba'
s[::-1]
'nocab ,gge'
s[2:0:-1]
'gg'
s[2::-1]
'gge'

formes idiomatiques#

s = [1, 2, 3]

# une copie simple
s[:]
'egg, bacon'
# copie renversée
s[::-1]
'nocab ,gge'

str pour manipuler du texte#

  • le type str est un cas particuliers de séquence

  • qui permet de manipuler du texte

un caractère ce n’est pas un octet

avec l’encodage le plus répandu aujourd’hui (UTF-8), les caractères ASCII tiennent sur un octet
mais ce sont les seuls: un é par exemple occupe 2 octets; un occupe 3 octets; un 🚀 occupe 4 octets

si vous avez besoin de manipuler des données brutes - typiquement si vous lisez un fichier binaire - il vous faut utiliser le type bytes que nous verrons plus tard

écrire une str de manière littérale#

  • une chaîne de caractères est définie de manière équivalente par des simples ou doubles guillemets (' ou ")

  • on peut ainsi facilement inclure un guillemet

# une chaine entre double quotes
# pas de souci pour les accents
print("c'est l'été")
c'est l'été
# entre simple quotes
print('on se dit "pourquoi pas"')
on se dit "pourquoi pas"

sur plusieurs lignes#

  • pour écrire une chaîne sur plusieurs lignes on utilise """ ou '''

# bien sûr ici vous pouvez remplacer les """ par '''

print("""et pour entrer plusieurs lignes,
ou bien des chaines avec des " et des '
c'est facile aussi""")
et pour entrer plusieurs lignes,
ou bien des chaines avec des " et des '
c'est facile aussi

chaînes de caractères accolées#

  • lorsque vous voulez entrer une chaine un peu longue

  • vous pouvez simplement accoler deux chaines dans votre source:

s = "le début" " et la fin"
print(s)
le début et la fin
s = ("une chaine trop longue"
     " pour tenir sur une ligne")
print(s)
une chaine trop longue pour tenir sur une ligne

les parenthèses sont importantes

notez bien les parenthèses dans ce deuxième exemple, car sinon c’est une erreur de syntaxe

échappements dans les chaines#

  • le caractère \ a un sens particulier dans les chaines écrites de manière littérale dans le programme

  • on écrit un retour chariot avec un \n

  • une tabulation avec \t

  • les guillemets peuvent être échappés (e.g. pour entrer un ' dans une chaine délimitée avec un ')

  • un backslash avec \\

  • enfin on peut aussi entrer des caractères exotiques par leur codepoint avec

    • \x \u \U

s = "deux\nlignes"
print(s)
deux
lignes
s = 'des\ttrucs\tespacés'
print(s)
des	trucs	espacés
# imaginons qu'on ait les deux sortes de guillemets
s = "simple' double\""
print(s)
simple' double"
s = 'backslash \\\tquote \''
print(s)
backslash \	quote '

raw-strings#

voici un problème commun, surtout sous Windows:

s = 'C:\Temp\test.txt'
print(s)
C:\Temp	est.txt
<>:1: SyntaxWarning: invalid escape sequence '\T'
<>:1: SyntaxWarning: invalid escape sequence '\T'
/tmp/ipykernel_658/895050390.py:1: SyntaxWarning: invalid escape sequence '\T'
  s = 'C:\Temp\test.txt'
  • \T n’existe pas comme échappement; Python interprète correctement \T

  • mais \t est compris comme une tabulation !!

Python-3.12

il semble même qu’en 3.12, un \T dans une chaine provoque carrément une erreur de syntaxe - ce qui est sans doute mieux au sens où au moins on détecte l’erreur plus tôt…

pour résoudre ce problème, on peut utiliser des double-backslash \\, mais ce n’est vraiment pas élégant
la bonne solution consiste à utiliser une “raw string”, dans laquelle les backslash ne sont pas interprétés

# pour créer une raw-string, simplement faire précéder le string d'un 'r'

s = r'C:\Temp\test1.bin'

print(s)
C:\Temp\test1.bin

préférez le / pour les chemins de fichier

notez que le plus souvent, vous pouvez aussi bien utiliser un / au lieu d’un \ dans les chemins de fichiers sous Windows, ce qui résoud tous les problèmes d’échappement

les raw-strings restent une feature bien pratique dans d’autres contextes, notamment avec les expressions régulières, que nous n’avons pas pu utiliser comme exemple ici puisqu’on n’en a pas encore parlé..

docstrings#

une docstring est une chaine littérale insérée au tout début du code d’une fonction (ou classe ou module) et qui sert à la documenter
on utilise souvent les triples guillemets pour cela

def double(n):
    """
    Returns the double of its input parameter

    The help message usually spans several lines
    """
    return 2*n
help(double)
Help on function double in module __main__:

double(n)
    Returns the double of its input parameter

    The help message usually spans several lines

opérations sur les str#

toutes les opérations des séquences#

que l’on a déjà vues :

s1 = 'abcdéfg'
s2 = 'bob'
len(s1)
7
# concaténation
s1 + s2
'abcdefbob'
'abcdefbob'
# slicing
s1[-1::-2]
'géca'
'=' * 30
'=============================='
# est-ce une sous-chaine ?
'cdé' in s1
True
# à quelle position ?
s1.index('cdé')
2

objet str non mutable#

par contre ATTENTION un str n’est pas mutable

try:
    s1[2] = 'x'
except TypeError as e:
    print("OOPS", e, type(e))
OOPS 'str' object does not support item assignment <class 'TypeError'>

méthodes sur les str#

strip()#

# j'ai lu une chaine dans un fichier
# je ne sais pas trop s'il y a des espaces à la fin
# et si la chaine contient un newline

dirty = "  des blancs au début et à la fin et un newline  \n"
dirty
'  des blancs au début et à la fin et un newline  \n'
# c'est la méthode la plus simple pour nettoyer
dirty.strip()
'des blancs au début et à la fin et un newline'

utilisez rstrip() pour nettoyer les lignes lues dans un fichier

lorsqu’on nettoie une ligne qu’on a lue dans un fichier, on peut envisager d’utiliser rstrip() qui ne nettoie qu’à droite, là où se situe le NEWLINE

split() et join()#

# une chaine à découper
s = "une phrase\nsur deux lignes"
s
'une phrase\nsur deux lignes'
# sans argument, split
# découpe selon les espaces
# et tabulations et newline
liste = s.split()
liste
['une', 'phrase', 'sur', 'deux', 'lignes']
# recoller les morceaux
"".join(liste)
'unephrasesurdeuxlignes'
# le plus souvent
" ".join(liste)
'une phrase sur deux lignes'
# ou n'importe quel autre séparateur
"+++".join(liste)
'une+++phrase+++sur+++deux+++lignes'

remplacements, recherches#

s = "une petite phrase"
s.replace('petite', 'grande')
'une grande phrase'
# l'index du premier caractère
s.find('hra')
12
s[12]
'h'

et plein d’autres..#

  • de nombreuses méthodes disponibles

  • personne ne retient l’intégralité des méthodes sur les types de base

  • le bon réflexe : chercher dans la dos Python qui est très bien faite

  • google les simples mots clés ‘python str’, vous trouvez

  • https://docs.python.org/3/library/stdtypes.html

un peu d’introspection (avancé)#

disons qu’on voudrait savoir combien de méthodes sont disponibles sur les chaines.

type("abc")
str
str
str
# 'str' est une variable prédéfinie, qui référence
# le type (la classe) de toutes les chaines
type("abc") is str
True
# du coup son type, c'est .. le type <type>
type(str)
type

un peu d’introspection…#

# peu importe... quoi qu'il en soit, dir(str) retourne la liste
# des noms de méthodes connues sur cette classe;
# regardons par exemple les premiers et les derniers
dir(str)[:2], dir(str)[-2:]
(['__add__', '__class__'], ['upper', 'zfill'])
# avec len() je peux savoir combien il y en a
len(dir(str))
81
# mais en fait, pour un décompte significatif
# on enlève celles dont le nom contient `__`
methods = [method for method in dir(str)
           if '__' not in method]
len(methods)
47
# est-ce que les chaines ont une méthode 'split' ?
'split' in methods
True