web-dev-qa-db-fra.com

Manière pythonique de créer une longue chaîne multiligne

J'ai une très longue requête. Je voudrais le scinder en plusieurs lignes en Python. Une façon de le faire en JavaScript consisterait à utiliser plusieurs phrases et à les joindre à un opérateur + (je sais, ce n'est peut-être pas le moyen le plus efficace de le faire, mais je ne m'inquiète pas vraiment des performances à ce stade. , juste la lisibilité du code). Exemple:

var long_string = 'some text not important. just garbage to' +
                  'illustrate my example';

J'ai essayé de faire quelque chose de similaire en Python, mais cela n'a pas fonctionné. J'ai donc utilisé \ pour scinder la longue chaîne. Cependant, je ne suis pas sûr que ce soit le seul/meilleur/meilleur moyen de le faire. Cela semble bizarre. Code actuel:

query = 'SELECT action.descr as "action", '\
    'role.id as role_id,'\
    'role.descr as role'\
    'FROM '\
    'public.role_action_def,'\
    'public.role,'\
    'public.record_def, '\
    'public.action'\
    'WHERE role.id = role_action_def.role_id AND'\
    'record_def.id = role_action_def.def_id AND'\
    'action.id = role_action_def.action_id AND'\
    'role_action_def.account_id = ' + account_id + ' AND'\
    'record_def.account_id=' + account_id + ' AND'\
    'def_id=' + def_id
1088
Pablo Mescher

Parlez-vous des chaînes multi-lignes? Facile, utilisez des guillemets triples pour les commencer et les terminer.

s = """ this is a very
        long string if I had the
        energy to type more and more ..."""

Vous pouvez également utiliser des guillemets simples (3 bien sûr au début et à la fin) et traiter la chaîne résultante s comme toute autre chaîne.

NOTE: Comme avec n'importe quelle chaîne, tout ce qui se situe entre les guillemets de début et de fin devient une partie de la chaîne. Cet exemple comporte donc un blanc (comme indiqué par @ root45). Cette chaîne contiendra également des blancs et des nouvelles lignes.

C'est à dire.,:

' this is a very\n        long string if I had the\n        energy to type more and more ...'

Enfin, on peut aussi construire de longues lignes dans Python comme ceci:

 s = ("this is a very"
      "long string too"
      "for sure ..."
     )

qui not inclura tous les espaces ou les nouvelles lignes (il s’agit d’un exemple délibéré montrant l’effet de la suppression des espaces):

'this is a verylong string toofor sure ...'

Aucune virgule requise, il suffit de placer les chaînes à assembler dans une paire de parenthèses et d’être sûr de prendre en compte les espaces et les nouvelles lignes nécessaires.

1877
Levon

Si vous ne voulez pas de chaîne multiligne mais que vous avez une longue chaîne simple, vous pouvez utiliser des parenthèses, assurez-vous simplement de ne pas inclure de virgule entre les segments de chaîne, ce sera alors un tuple.

query = ('SELECT   action.descr as "action", '
         'role.id as role_id,'
         'role.descr as role'
         ' FROM '
         'public.role_action_def,'
         'public.role,'
         'public.record_def, '
         'public.action'
         ' WHERE role.id = role_action_def.role_id AND'
         ' record_def.id = role_action_def.def_id AND'
         ' action.id = role_action_def.action_id AND'
         ' role_action_def.account_id = '+account_id+' AND'
         ' record_def.account_id='+account_id+' AND'
         ' def_id='+def_id)

Dans une instruction SQL telle que celle que vous construisez, des chaînes multilignes conviendraient également. Mais si les espaces supplémentaires qu'une chaîne multiligne contiendrait poseraient un problème, ce serait un bon moyen de réaliser ce que vous voulez.

160
Jesse

Couper les lignes de \ fonctionne pour moi. Voici un exemple:

longStr = "This is a very long string " \
        "that I wrote to help somebody " \
        "who had a question about " \
        "writing long strings in Python"
119
amphibient

Je me suis trouvé heureux avec celui-ci:

string = """This is a
very long string,
containing commas,
that I split up
for readability""".replace('\n',' ')
45
Eero Aaltonen

Je trouve que lorsque vous construisez de longues chaînes, vous faites habituellement quelque chose comme une requête SQL, auquel cas il vaut mieux:

query = ' '.join((  # note double parens, join() takes an iterable
    "SELECT foo",
    "FROM bar",
    "WHERE baz",
))

Ce que Levon a suggéré est bon, mais pourrait être vulnérable aux erreurs:

query = (
    "SELECT foo"
    "FROM bar"
    "WHERE baz"
)

query == "SELECT fooFROM barWHERE baz"  # probably not what you want
35
darkfeline

Vous pouvez également concaténer des variables en utilisant la notation "" ":

foo = '1234'

long_string = """fosdl a sdlfklaskdf as
as df ajsdfj asdfa sld
a sdf alsdfl alsdfl """ +  foo + """ aks
asdkfkasdk fak"""

EDIT: Trouver un meilleur moyen, avec nommés params et .format ():

body = """
<html>
<head>
</head>
<body>
    <p>Lorem ipsum.</p>
    <dl>
        <dt>Asdf:</dt>     <dd><a href="{link}">{name}</a></dd>
    </dl>
    </body>
</html>
""".format(
    link='http://www.asdf.com',
    name='Asdf',
)

print(body)
26
gjgjgj

Dans Python> = 3.6, vous pouvez utiliser littéraux de chaîne formatés (chaîne f)

query= f'''SELECT   action.descr as "action"
    role.id as role_id,
    role.descr as role
    FROM
    public.role_action_def,
    public.role,
    public.record_def,
    public.action
    WHERE role.id = role_action_def.role_id AND
    record_def.id = role_action_def.def_id AND
    action.id = role_action_def.action_id AND
    role_action_def.account_id = {account_id} AND
    record_def.account_id = {account_id} AND
    def_id = {def_id}'''
21
Vlad Bezden

Cette approche utilise:

  • une seule barre oblique inverse pour éviter un saut de ligne initial
  • presque pas de ponctuation interne en utilisant une chaîne triple
  • élimine l'indentation locale en utilisant le habillage de texte Module inspect
  • utilise python _ 3.6 interpolation de chaîne formatée ('f') pour les variables account_id et def_id.

Cette façon me semble la plus pythonique.

# import textwrap  # See update to answer below
import inspect

# query = textwrap.dedent(f'''\
query = inspect.cleandoc(f'''
    SELECT action.descr as "action", 
    role.id as role_id,
    role.descr as role
    FROM 
    public.role_action_def,
    public.role,
    public.record_def, 
    public.action
    WHERE role.id = role_action_def.role_id AND
    record_def.id = role_action_def.def_id AND
    action.id = role_action_def.action_id AND
    role_action_def.account_id = {account_id} AND
    record_def.account_id={account_id} AND
    def_id={def_id}'''
)

Mise à jour: 29/01/2019 Incorporer la suggestion de @ ShadowRanger d'utiliser inspect.cleandoc au lieu de textwrap.dedent

19

Par exemple:

sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1={} "
       "and condition2={}").format(1, 2)

Output: 'select field1, field2, field3, field4 from table 
         where condition1=1 and condition2=2'

si la valeur de condition doit être une chaîne, vous pouvez faire comme ceci:

sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1='{0}' "
       "and condition2='{1}'").format('2016-10-12', '2017-10-12')

Output: "select field1, field2, field3, field4 from table where
         condition1='2016-10-12' and condition2='2017-10-12'"
11
pangpang

Personnellement, je trouve que le texte suivant est le meilleur moyen (simple, sûr et Pythonic) d’écrire des requêtes SQL brutes en Python, en particulier lors de l’utilisation de module sqlite3 de Python :

query = '''
    SELECT
        action.descr as action,
        role.id as role_id,
        role.descr as role
    FROM
        public.role_action_def,
        public.role,
        public.record_def,
        public.action
    WHERE
        role.id = role_action_def.role_id
        AND record_def.id = role_action_def.def_id
        AND action.id = role_action_def.action_id
        AND role_action_def.account_id = ?
        AND record_def.account_id = ?
        AND def_id = ?
'''
vars = (account_id, account_id, def_id)   # a Tuple of query variables
cursor.execute(query, vars)   # using Python's sqlite3 module

Avantages

  • Un code simple et net (Pythonic!)
  • Safe from SQL injection
  • Compatible avec Python 2 et Python 3 (c'est Pythonic après tout)
  • Aucune concaténation de chaîne requise
  • Pas besoin de s'assurer que le caractère le plus à droite de chaque ligne est un espace

Les inconvénients

  • Étant donné que les variables de la requête sont remplacées par l’espace réservé ?, il peut devenir un peu difficile de garder trace de ce que ? doit être remplacé par quelle variable Python quand il y a beaucoup d'entre eux dans la requête.
9
Faheel Ahmad

Je trouve textwrap.dedent le meilleur pour les longues chaînes comme décrit ici :

_def create_snippet():
    code_snippet = textwrap.dedent("""\
        int main(int argc, char* argv[]) {
            return 0;
        }
    """)
    do_something(code_snippet)
_
8
fredrik

D'autres ont déjà mentionné la méthode des parenthèses, mais j'aimerais ajouter qu'avec les parenthèses, les commentaires en ligne sont autorisés.

Commentaire sur chaque fragment:

nursery_rhyme = (
    'Mary had a little lamb,'          # Comments are great!
    'its fleece was white as snow.'
    'And everywhere that Mary went,'
    'her sheep would surely go.'       # What a pesky sheep.
)

Commentaire non autorisé après la suite:

Lors de l'utilisation de continuations de lignes obliques inverses (\), les commentaires ne sont pas autorisés. Vous recevrez une erreur SyntaxError: unexpected character after line continuation character.

nursery_rhyme = 'Mary had a little lamb,' \  # These comments
    'its fleece was white as snow.'       \  # are invalid!
    'And everywhere that Mary went,'      \
    'her sheep would surely go.'
# => SyntaxError: unexpected character after line continuation character

Meilleurs commentaires pour les chaînes Regex:

Basé sur l'exemple de https://docs.python.org/3/library/re.html#re.VERBOSE ,

a = re.compile(
    r'\d+'  # the integral part
    r'\.'   # the decimal point
    r'\d*'  # some fractional digits
)
# Using VERBOSE flag, IDE usually can't syntax highight the string comment.
a = re.compile(r"""\d +  # the integral part
                   \.    # the decimal point
                   \d *  # some fractional digits""", re.X)
5
ddrscott

J'utilise habituellement quelque chose comme ça:

text = '''
    This string was typed to be a demo
    on how could we write a multi-line
    text in Python.
'''

Si vous souhaitez supprimer les espaces vides ennuyeux de chaque ligne, procédez comme suit:

text = '\n'.join(line.lstrip() for line in text.splitlines())
4
Israel Barth Rubio

Votre code actuel ne devrait pas fonctionner, il manque des espaces à la fin des "lignes" (par exemple: role.descr as roleFROM...)

Il y a des triples cotes pour les chaînes multilignes:

string = """line
  line2
  line3"""

Il contiendra les sauts de ligne et les espaces supplémentaires, mais pour SQL ce n'est pas un problème.

3
Karoly Horvath

Vous pouvez également placer l'instruction sql dans un fichier séparé action.sql et la charger dans le fichier py avec

with open('action.sql') as f:
   query = f.read()

Ainsi, les instructions sql seront séparées du code python. S'il existe des paramètres dans l'instruction SQL qui doivent être remplis à partir de python, vous pouvez utiliser le formatage de chaîne (comme% s ou {champ}).

2
mrijken

"À la" Scala moyen (mais je pense que c'est le moyen le plus pythonique comme l'exige l'OQ):

description = """
            | The intention of this module is to provide a method to 
            | pass meta information in markdown_ header files for 
            | using it in jinja_ templates. 
            | 
            | Also, to provide a method to use markdown files as jinja 
            | templates. Maybe you prefer to see the code than 
            | to install it.""".replace('\n            | \n','\n').replace('            | ',' ')

Si vous voulez une chaîne finale sans lignes de saut, il suffit de mettre \n au début du premier argument du deuxième remplacement:

.replace('\n            | ',' ')`.

Remarque: la ligne blanche entre "... modèles". et "En outre, ..." nécessite un espace après le |.

2
victe

J'utilise une fonction récursive pour construire des requêtes SQL complexes. Cette technique peut généralement être utilisée pour construire de grandes chaînes tout en maintenant la lisibilité du code.

# Utility function to recursively resolve SQL statements.
# CAUTION: Use this function carefully, Pass correct SQL parameters {},
# TODO: This should never happen but check for infinite loops
def resolveSQL(sql_seed, sqlparams):
    sql = sql_seed % (sqlparams)
    if sql == sql_seed:
        return ' '.join([x.strip() for x in sql.split()])
    else:
        return resolveSQL(sql, sqlparams)

P.S: Jetez un coup d’œil à l’impressionnante bibliothèque python-sqlparse pour imprimer de jolies requêtes SQL si nécessaire. http://sqlparse.readthedocs.org/fr/latest/api/#sqlparse.format

1
Sandeep

Une autre option qui, à mon avis, est plus lisible lorsque le code (par exemple, une variable) est en retrait et que la chaîne de sortie doit être une ligne (sans nouvelle ligne):

def some_method():

    long_string = """
a presumptuous long string 
which looks a bit nicer 
in a text editor when
written over multiple lines
""".strip('\n').replace('\n', ' ')

    return long_string 
1
Eyal Levin

tl; dr: Utilisez """\ et """ pour envelopper la chaîne , comme dans

string = """\
This is a long string
spanning multiple lines.
"""

À partir de la documentation officielle python :

Les littéraux de chaîne peuvent s'étendre sur plusieurs lignes. L'une des méthodes consiste à utiliser des guillemets triples: "" "..." "" ou "" ... ". Les fins de lignes sont automatiquement incluses dans la chaîne, mais il est possible d’éviter cela en ajoutant un\à la fin de la ligne. L'exemple suivant:

print("""\
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
""")

produit le résultat suivant (notez que la nouvelle ligne initiale n'est pas incluse):

Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
0
Flow

À partir de la documentation officielle python :

Les littéraux de chaîne peuvent s'étendre sur plusieurs lignes. L'une des méthodes consiste à utiliser des guillemets triples: "" "..." "" ou "" ... ". Les fins de lignes sont automatiquement incluses dans la chaîne, mais il est possible d’éviter cela en ajoutant un\à la fin de la ligne. L'exemple suivant:

print("""\
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
""")

produit le résultat suivant (notez que la nouvelle ligne initiale n'est pas incluse):

0
Flow

Hey, essayez quelque chose comme ça, espérons que cela fonctionne, comme dans ce format, il vous retournera une ligne continue comme si vous aviez demandé avec succès à propos de cette propriété "

"message": f'you have successfully inquired about '
           f'{enquiring_property.title} Property owned by '
           f'{enquiring_property.client}'
0
Kelvin Onkundi

J'aime cette approche parce qu'elle privilégie la lecture. Dans les cas où nous avons de longues chaînes, il n'y a aucun moyen! En fonction du niveau d'indentation dans lequel vous vous trouvez et toujours limité à 80 caractères par ligne ... Bon ... Pas besoin de dire autre chose. À mon avis, les guides de style python sont encore très vagues. J'ai choisi l'approche @Eero Aaltonen car elle privilégie la lecture et le bon sens. Je comprends que les guides de style devraient nous aider et non pas gâcher nos vies. Merci!

class ClassName():
    def method_name():
        if condition_0:
            if condition_1:
                if condition_2:
                    some_variable_0 =\
"""
some_js_func_call(
    undefined, 
    {
        'some_attr_0': 'value_0', 
        'some_attr_1': 'value_1', 
        'some_attr_2': '""" + some_variable_1 + """'
    }, 
    undefined, 
    undefined, 
    true
)
"""
0
Eduardo Lucio