Comment marchent les “raw strings” en Python ?


Dans certains tutos, notamment ceux sur les expressions rationnelles, on recommande d’utiliser les “raw strings”, en mettant un “r” devant la déclaration de la chaîne de caractères.

Par exemple :

'1?\d\d?'

Devient :

r'1?\d\d?'

À quoi cela sert-il ?

Voyons d’abord à quoi cela ne sert pas

  • Créer un type de string particulier. Il n’y a rien de tel qu’un type “raw string” en Python. La chaîne résultante est une chaîne ordinaire
  • Créer une chaîne destinée aux regexes. On utilise particulièrement la notation ‘r’ avec les regexs, mais ce n’est pas un type dédié

‘r’ est juste un modificateur, une sorte de paramètre. En effet, quand vous écrivez 'Salut !\n', vous n’écrivez PAS la chaîne “Salut ![LB]”. Vous dites à Python de créer un objet chaîne, de la même manière que vous lui demanderiez une instanciation en faisant MaClasse().

'Salut !\n' est juste une notation demandant de créer une chaîne, pas la chaîne elle-même. Cette notation dit à Python : À partir de cet instant dans le programme, tu vas créer un objet chaîne en mémoire, et voici les paramètres que je te donne pour le créer. La notation étant différente, on a l’illusion d’écrire la chaîne soi-même, mais en fait ce n’est pas différent d’un appel de fonction.

Quand vous faites 'Salut !\n', vous dites plus précisément à Python : Instancie un objet de type string, met les caractères S, a, l, u, t, espace et point d’exclamation dedans, suivi d’un saut de ligne. Cette notation, pour se faciliter la vie, permet de décrire “saut de ligne” en écrivant '\n'. Python analyse donc votre chaîne, cherche toutes les combinaisons de caractères spéciaux comme '\n', '\t', etc, et quand il crée l’objet en mémoire, il ajoute un saut de ligne ou une tabulation, et pas les caractères ‘\’ puis ‘n’ ou ‘t’.

Que se passe-t-il si vous voulez réellement ajouter ‘\’ et ‘n’ ?

Il faut utiliser une autre notation, l’échappement. On utilise un ‘\’ pour dire à Python: créer cet objet en mémoire, mais à cet endroit, ne tient pas compte de la combinaison de caractères spéciaux.

Ceci affiche un saut de ligne : print('\n est le caractère de saut de ligne')

Ceci affiche ‘\’, ‘n’ puis la phrase : print('\\n est le caractère de saut de ligne')

Mais il existe des cas où c’est très fastidieux et illisible. Notamment les expressions rationnelles, où les ‘\’ font partie intégrante du système.

Un exemple simple, vous voulez “C:\Program Files” dans une phrase :

Votre regex devra contenir ‘\P’. Sauf qu’en regex, ‘\P’ est un symbole spécial, donc il faut l’échapper, vous aurez donc ‘\\P’. Sauf qu’en Python, il faut échapper les ‘\’ pour qu’ils ne soient pas considérés comme caractères spéciaux, vous aurez donc ‘\\\\P’. Sur une regex complexe, ça devient vite très moche, et très dur à déboguer.

C’est là qu’intervient le modificateur ‘r’, qui précise à Python : quand tu vas créer cette chaîne en mémoire, met ces caractères dedans littéralement et considère qu’il n’y a aucune combinaison de caractères spéciaux.

En clair r'\n' sera ‘\’ puis ‘n’. Python ne va tout simplement par parser la chaîne, il va l’utiliser littéralement, d’où le “raw” string.

Il n’y a donc rien de spécial dans une chaîne créée avec le modificateur ‘r’, c’est une chaîne normale, qui est instanciée sans réfléchir par Python, sans chercher à être malin et comprendre des notations spéciales.

Ça ne veut pas dire qu’on ne peut pas avoir des sauts de ligne dans une chaîne créée avec ‘r’ :

>>> print(r"""1 + 1 = 
... 42""")
1 + 1
42

Ça veut juste dire que les notations spéciales ne seront pas analysées :

>>> print('1 + 1 =\n 42')
1 + 1 =
42
>>> print(r'1 + 1 =\n 42')
1 + 1 =\n 42

Il est facile de s’embrouiller les pinceaux à cause du shell Python :

>>> 'test\n'
'test\n'
>>> r'test\n'
'test\\n'
>>> print('test\n')
test
 
>>> print(r'test\n')
test\n

Il faut savoir que quand on affiche un objet sans utiliser print() dans le shell, ce dernier essaye de vous afficher une représentation de l’objet de telle sorte qu’il puisse être copié et collé par un dev, et recréer le même objet. En revanche, print() va afficher du texte formaté pour être lisible par un utilisateur final.

Enfin, les combinaisons de préfixes peuvent ou non être compatibles:

>>> ur'test\n' 
  File "<ipython-input-6-334281889b3a>", line 1
    ur'test\n'
           ^
SyntaxError: invalid syntax
>>> rb'test\n'
    b'test\\n'

Donc si vous faites un code compatible Python 2 et 3 en même temps, faites gaffe.

Si les fstrings sont acceptées, ‘f’ sera compatible avec ‘r’.

7 thoughts on “Comment marchent les “raw strings” en Python ?

  • Sam Post author

    Pour tous ceux qui viennent du net en cherchant:

    python à quoi sert le “r”

    C’est ici !

  • Lujeni

    Question annexe, l’utilisation d’un raw string permet-il un gain en performance ? Merci pour l’article.

  • Sam Post author

    Aucune idée. Mais si tu cherches à gagner des performances sur l’initialisation d’une string, Python n’est pas le langage qu’il te faut. A ce stade de besoin (rare) de perf, autant passer au C. Quitte à faire un binding Python par dessus.

  • Anne Onyme

    Plop!

    Comme hier, je vous propose de profiter de ce petit dépoussiérage d’été pour corriger les dernières fautes de frappe/d’orthographe/de grammaire, comme ça l’article sera encore plus parfait que parfait.

    (C’est ma façon à moi de contribuer. J’essayerai de vous suivre dans le dépoussiérage…)

    Je propose:

    * “expressions rationelles” -> “expressions rationnelles”;

    * “les regexs” -> “les regexes”

    * “elle même” -> “elle-même”;

    * “charactères” -> “caractères”;

    * “les expression rationnelles” -> “les expressions rationnelles”;

    * “Saut qu’en regex” -> “Saut qu’en regex”;

    * “special” -> “spécial”;

    * “modifieur” -> “modificateur” (à corriger 3 fois);

    * “En clait” -> “En clair”

    * “rien de spécial à dans une chaîne” -> “rien de spécial dans une chaîne”;

    * “de telle sorte qu’il puisse être copié et collé par un dev, et recréer le même objet” -> “de telle sorte qu’il puisse être copié et collé par un dev pour recréer le même objet”.

  • YCL1

    Dans un cas très particulier vous pourriez avoir besoin de reconvertir un raw string en string normal, il existe ceci :

    print(r”_\n\t_\n\t\t_\n”.encode(‘utf-8’).decode(‘unicode-escape’))

  • Sam Post author

    Il n’existe pas de type “raw string”. Ton code ne converti pas une “raw string”, il converti la notation utilisée par python pour représenter des caractères spéciaux dans une chaînes en ces caractères spéciaux.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.