web-dev-qa-db-fra.com

Indentation correcte pour Python chaînes multilignes

Quelle est l'indentation appropriée pour Python chaînes multilignes dans une fonction?

    def method():
        string = """line one
line two
line three"""

ou

    def method():
        string = """line one
        line two
        line three"""

ou autre chose?

Il est plutôt étrange de voir la chaîne suspendue à l'extérieur de la fonction dans le premier exemple.

424
ensnare

Vous voulez probablement vous aligner avec le """

def foo():
    string = """line one
             line two
             line three"""

Puisque les nouvelles lignes et les espaces sont inclus dans la chaîne elle-même, vous devrez la post-traiter. Si vous ne voulez pas faire cela et que vous avez beaucoup de texte, vous voudrez peut-être le stocker séparément dans un fichier texte. Si un fichier texte ne fonctionne pas bien pour votre application et que vous ne voulez pas post-traiter, j'irais probablement avec

def foo():
    string = ("this is an "
              "implicitly joined "
              "string")

Si vous souhaitez post-traiter une chaîne multiligne pour supprimer les parties inutiles, vous devez envisager le module textwrap ou la technique de post-traitement des docstrings présentée dans PEP 257 :

def trim(docstring):
    if not docstring:
        return ''
    # Convert tabs to spaces (following the normal Python rules)
    # and split into a list of lines:
    lines = docstring.expandtabs().splitlines()
    # Determine minimum indentation (first line doesn't count):
    indent = sys.maxint
    for line in lines[1:]:
        stripped = line.lstrip()
        if stripped:
            indent = min(indent, len(line) - len(stripped))
    # Remove indentation (first line is special):
    trimmed = [lines[0].strip()]
    if indent < sys.maxint:
        for line in lines[1:]:
            trimmed.append(line[indent:].rstrip())
    # Strip off trailing and leading blank lines:
    while trimmed and not trimmed[-1]:
        trimmed.pop()
    while trimmed and not trimmed[0]:
        trimmed.pop(0)
    # Return a single string:
    return '\n'.join(trimmed)
434
Mike Graham

La fonction textwrap.dedent permet de commencer par l’indentation correcte dans la source , puis de la supprimer du texte. Avant utilisation.

Le compromis, comme l'ont noté d'autres personnes, est qu'il s'agit d'un appel de fonction supplémentaire au littéral. tenez compte de cela lorsque vous décidez où placer ces littéraux dans votre code.

_import textwrap

def frobnicate(param):
    """ Frobnicate the scrognate param.

        The Weebly-Ruckford algorithm is employed to frobnicate
        the scrognate to within an inch of its life.

        """
    prepare_the_comfy_chair(param)
    log_message = textwrap.dedent("""\
            Prepare to frobnicate:
            Here it comes...
                Any moment now.
            And: Frobnicate!""")
    weebly(param, log_message)
    ruckford(param)
_

La fin _\_ dans le littéral du message de journal permet de s’assurer que le saut de ligne n’est pas dans le littéral; De cette façon, le littéral ne commence pas par une ligne vide, mais par la ligne complète suivante.

La valeur renvoyée par _textwrap.dedent_ est la chaîne en entrée avec toutes les indentations d'espacement les plus communes précédantes supprimées sur chaque ligne de la chaîne. La valeur _log_message_ ci-dessus sera donc:

_Prepare to frobnicate:
Here it comes...
    Any moment now.
And: Frobnicate!
_
233
bignose

Utilisez inspect.cleandoc comme suit:

_def method():
    string = inspect.cleandoc("""
        line one
        line two
        line three""")
_

L'indentation relative sera maintenue comme prévu.

Remarque: Il est judicieux d'indenter des blocs de code logiques dans son contexte associé afin de clarifier la structure. Par exemple. la chaîne multiligne appartenant à la variable string.

25
wihlke

Une option qui semble manquer aux autres réponses (mentionnée au fond d'un commentaire de naxa) est la suivante:

def foo():
    string = ("line one\n"          # Add \n in the string
              "line two"  "\n"      # Add "\n" after the string
              "line three\n")

Cela permettra un alignement correct, joignera les lignes implicitement et conservera le décalage de ligne, ce qui est pour moi l’une des raisons pour lesquelles j’aimerais quand même utiliser des chaînes multilignes.

Il ne nécessite aucun post-traitement, mais vous devez ajouter manuellement le \n à un endroit donné où vous voulez que la ligne se termine. Soit en ligne ou en tant que chaîne séparée après. Ce dernier est plus facile à copier-coller.

22
holroy

Quelques autres options. Dans Ipython avec pylab activé, dedent est déjà dans l’espace de noms. J'ai vérifié et c'est de matplotlib. Ou il peut être importé avec:

from matplotlib.cbook import dedent

Dans la documentation, il est indiqué qu’il est plus rapide que l’équivalent textwrap et que lors de mes tests sous ipython, il est effectivement 3 fois plus rapide en moyenne avec mes tests rapides. Il a également l'avantage de supprimer toutes les lignes vides en tête, ce qui vous permet d'être flexible dans la construction de la chaîne:

"""
line 1 of string
line 2 of string
"""

"""\
line 1 of string
line 2 of string
"""

"""line 1 of string
line 2 of string
"""

L'utilisation de Matplotlib dédiée à ces trois exemples donnera le même résultat raisonnable. La fonction déduction textwrap aura une première ligne vide avec le 1er exemple.

Le désavantage évident est que textwrap est dans une bibliothèque standard alors que matplotlib est un module externe.

Certains compromis ici ... les fonctions dédiées rendent votre code plus lisible là où les chaînes sont définies, mais nécessitent un traitement ultérieur pour obtenir la chaîne au format utilisable. Dans docstrings, il est évident que vous devez utiliser le bon retrait car la plupart des utilisations de docstring effectueront le traitement requis.

Lorsque j'ai besoin d'une chaîne non longue dans mon code, je trouve le code ci-après, certes laid, dans lequel je laisse la chaîne longue tomber de l'indentation. Il échoue définitivement sur "Le beau est meilleur que le laid", mais on pourrait dire que c'est plus simple et plus explicite que l'alternative dédent.

def example():
    long_string = '''\
Lorem ipsum dolor sit amet, consectetur adipisicing
elit, sed do eiusmod tempor incididunt ut labore et
dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip.\
'''
    return long_string

print example()
17
Joop

Si vous voulez une solution rapide et facile et que vous évitiez de taper des nouvelles lignes, vous pouvez opter pour une liste, par exemple:

def func(*args, **kwargs):
    string = '\n'.join([
        'first line of very long string and',
        'second line of the same long thing and',
        'third line of ...',
        'and so on...',
        ])
    print(string)
    return
5
steabert

Je préfère

    def method():
        string = \
"""\
line one
line two
line three\
"""

ou

    def method():
        string = """\
line one
line two
line three\
"""
4
lk_vc

Mes deux cents, échapper à la fin de la ligne pour obtenir les tirets:

def foo():
    return "{}\n"\
           "freq: {}\n"\
           "temp: {}\n".format( time, freq, temp )
3
Simon

La première option est la bonne - avec l'indentation incluse. C'est dans le style python - fournit la lisibilité du code.

Pour l'afficher correctement:

print string.lstrip()
1
Bog Dia

Je suis venu ici à la recherche d'un simple 1-liner pour supprimer/corriger le niveau d'identification de la docstring pour l'impression, sans le rendre désordonné, par exemple en le faisant "accrocher" en dehors de la fonction "dans le script.

Voici ce que j'ai fini par faire:

import string
def myfunction():

    """
    line 1 of docstring
    line 2 of docstring
    line 3 of docstring"""

print str(string.replace(myfunction.__doc__,'\n\t','\n'))[1:] 

Évidemment, si vous indentez avec des espaces (par exemple 4) plutôt que la touche de tabulation, utilisez quelque chose comme ceci à la place:

print str(string.replace(myfunction.__doc__,'\n    ','\n'))[1:]

Et vous n'avez pas besoin de supprimer le premier caractère si vous préférez que votre docstring ressemble à ceci:

    """line 1 of docstring
    line 2 of docstring
    line 3 of docstring"""

print string.replace(myfunction.__doc__,'\n\t','\n') 
1
James Gowdy

Cela dépend de la façon dont vous voulez que le texte soit affiché. Si vous souhaitez que tout soit aligné à gauche, formatez-le comme dans le premier extrait de code ou parcourez les lignes en supprimant tout l'espace.

0

Pour les chaînes, vous pouvez juste après traiter la chaîne. Pour docstrings, vous devez plutôt traiter la fonction après. Voici une solution pour les deux qui est toujours lisible.

class Lstrip(object):
    def __rsub__(self, other):
        import re
        return re.sub('^\n', '', re.sub('\n$', '', re.sub('\n\s+', '\n', other)))

msg = '''
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
      commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
      velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
      cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
      est laborum.
      ''' - Lstrip()

print msg

def lstrip_docstring(func):
    func.__doc__ = func.__doc__ - Lstrip()
    return func

@lstrip_docstring
def foo():
    '''
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
    tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
    veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
    commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
    velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
    cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
    est laborum.
    '''
    pass


print foo.__doc__
0
geckos