% ou format() en Python ? 26


On a reçu un mail du genre :

Salut les mecs!

Je me demandais si il valait mieux utiliser format() ou % quand on veut insérer une variable dans une chaîne?
Je comprend pas vraiment quelle est la différence entre les deux…l’un est-il plus rapide? Plus fiable?

Merci les mecs!

Je suppose que d’autres personnes se posent la même question, du coup je poste ça là une bonne fois pour toutes.

En résumé : avec le recul, il n’y en a pas de meilleur. C’est une question de facilité d’usage et de lisibilité.

En effet, % était parti pour être déprécié en Python 3, mais ce n’est jamais arrivé, pour plusieurs raisons :

  • % est souvent plus court à taper.
  • le module logging utilise toujours ce format.
  • avec Python 3.5, le type bytes va de nouveau utiliser cet opérateur pour le formatage.

Si votre formatage est simple ou si vous utilisez des bytes ou le module logging, utilisez %:

>>> "je suis hyper %s. Brouuuuahhh" % "content"
    'je suis hyper content. Brouuuuahhh'

L’équivalent avec format() serait :

>>>  "je suis hyper {}. Brouuuuahhh".format("content")
    'je suis hyper content. Brouuuuahhh'

On y gagne pas, c’est plus long à taper car il y a plus de lettres mais aussi parce que qu’il y a beaucoup plus de caractères spéciaux à atteindre sur son clavier : {}.() contre %.

On table ici sur la facilité et la rapidité d’usage.

Si votre formatage a beaucoup de variables, que vos variables sont déjà dans un dictionnaire, que votre texte est long ou que vous avez besoin de formats avancés, utilisez format():

"{value}{unit} ({time:%H:%M:%S})".format(value=3, unit="ppm", time=datetime.now())
'3ppm (10:53:04)'

L’équivalent avec % serait:

date = datetime.now().strftime("%H:%M:%S")
"%(value)s%(unit)s (%(time)s)" % {"value": 3, "unit": "ppm", "time": date}

Moins lisible, plus verbeux. format() gagne toujours dès qu’on a un message complexe. Mais ça n’arrive pas aussi souvent qu’on ne le pense, du coup % a encore de beaux jours devant lui.

Avec Python 3.6 arrivera une nouvelle manière de faire, les f-strings. Si vous avez la chance d’utiliser la 3.6, les f-strings peuvent remplacer avantageusement la plupart des formes ci-dessus :

>>> value = 3
>>> unit = "ppm"
>>> f'{value}{unit} ({datetime.now():%H:%M:%S})'
'3ppm (10:53:54)'

Mais il faudra attendre 2016…

26 thoughts on “% ou format() en Python ?

  • Etienne
    print u"Moi %s {0} %s chier".format("me") % (u"{evol} %s ".format(evol=u"l'évolution"), u"fait") % u"ça"
  • Etienne

    Pardon, c’est pas très lisible:

    print u"Moi %s {0} %s chier".format("me")\
     % (u"{evol} %s ".format(evol=u"l'évolution")\
    , u"fait") % u"ça"
  • Romuald

    Une autre raison que “c’est comme ça qu’il faut faire” ?

    % marche aussi en python 3

    Accessoirement certaines syntaxes syntaxe de .format() sont valides en python 3, mais pas en python 2.6

    Exemple :

    "welcome {}".format("bar")
  • mothsART

    Les 2 ne sont pas plutôt complémentaires sur python 3?

    D’ailleurs :
    print('yes %s can!' % 'we')
    marche très bien sur la 3.3!

  • Sam Post author

    Wow, 10 ans de Python et je viens d’apprendre que “%” est conservé en Python 3. J’étais persuadé qu’il était deprecated.

    Bon, ça vallait le coup de faire l’article rien que pour ça.

    Merci de m’apprendre des trucs tous les jours. Et merci de le faire aussi gentiment.

  • Morgotth

    Oh le mauvais !!! *lapidation*

    Par contre je viens d’apprendre de @Romuald que 2.6 ne supportait pas toutes les syntaxes … Content d’avoir toujours utilisé % pour les simples remplacements :)

  • kontre

    J’ai cru lire que % était un chouilla plus rapide, ne serait-ce que parce qu’il a moins de fonctionnalités.

    Mais % c’est l’héritage des langages C-like, alors que .format() c’est plus pythonesque. (mais je suis moi aussi une grosse feignasse, à vrai dire)

  • Fred

    Je ne dis pas que format() n’est pas utile mais perso je n’en ai pas encore eu l’utilité et le % me convient très bien. Clair, rapide à taper et facile à relire et en plus conservé en Python3.
    Pourquoi alors se compliquer la vie…?

  • Syl

    Mais alors, comment utiliser format si on a un {} dans la chaine à manipuler?

    Exemple:


    In [20]: "Je veux inserer [0] dans une chaine contenant {zobi=lamouche}".format("ceci")
    ---------------------------------------------------------------------------
    KeyError Traceback (most recent call last)
    in ()
    ----> 1 "Je veux inserer [0] dans une chaine contenant {zobi=lamouche}".format("ceci")

    KeyError: 'zobi=lamouche'

  • Sam Post author
    >>> "Je veux inserer {0} dans une chaine contenant {{zobi=lamouche}}".format("ceci")
    u'Je veux inserer ceci dans une chaine contenant {zobi=lamouche}'
  • Romain

    format, c’est quand même bien quand dans une substitution, on veut utiliser plusieurs fois un terme

    In [2]: print('{0} {0} {0}'.format('bon'))
    bon bon bon

  • Sam Post author

    C’est aussi mieux pour les variables nommées. %(nom)s c’est plus chiant à tapper que {nom}

  • Stéphane

    Juste une coquille (deux corrections):
    feinénants -> fainéants

  • Sam Post author

    C’est ce que je croyais aussi, mais en fait il est toujours là dans les dernières versions de Python 3.

  • ice3

    Pour les usages plus avancés, il y a même un DSL de formattage dans “format”

    Du coup, on peut séparer la représentation des données, pour les nombres :

    Pour faire du padding

     
    a = "{:02}.png"
     
    a.format(1)  # 01.png
     
    a.format(30) # 30.png

    Ca évite d’utiliser les “zfills” et companie

    On peut même faire des conversions de base pour les nombres (ici du décimal au binaire) :

     
    a = "{:b}"
     
    a.format(50) # '110010'

    Plus d’infos : https://docs.python.org/2/library/string.html#format-specification-mini-language

  • Fred

    Je suis revenu sur cet article suite à la mise à jour annoncée par mail.

    Juste pour rajouter 2 remarques

    1) concernant la remarque de Romain sur le fait d’utiliser plusieurs fois le même terme, on peut faire aussi la même chose avec %

    => print “%(x)s %(x)s %(x)s” % {“x” : “bon”}

    2) l’équivalent qui affiche la date possède une variable inutile

    => “%(value)s%(unit)s (%(time)s)” % {“value”: 3, “unit”: “ppm”, “time”: datetime.now().strftime(“%H:%M:%S”)}

    Ben oui, quand on veut être honnête on essaye d’écrire un code similaire au-moins dans ses variables ;)

  • Sam Post author

    Dans ton dernier exemple, la ligne est énorme et illisible, et viole tellement le PEP 8 que Guido hésite a porter plainte pour agression sexuelle. Une ligne comme ça, on la met forcément sur deux lignes.

  • Larvis

    Cela peut aussi servir :

    >>> vars = ['aaa', 'bbb']
          >>>"3a=%s 3b=%s" % tuple(vars)
             '3a=aaa 3b=bbb'

    Quel est l’équivalent avec % ?

  • louis

    Pour un langage qui proclamait : “There should be one– and preferably only one –obvious way to do it.”, c’est un peu dommage d’avoir maintenant 2 manières presque parfaitement équivalentes de faire la même chose, et qui ne se démarquent que par l’habitude d’utilisation, la place des touches sur le clavier ou la lisibilité dans de très rares cas.

  • Sam Post author

    C’est tout le problème de l’innovation et la compatibilité. Si tu veux les deux, ce que Python arrive à faire assez brillament, tu vas forcément payer le prix d’un héritage en API.

Leave a comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

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