创建长的多行字符串的Pythonic方法


1303

我有一个很长的查询。我想在Python中将其分成几行。用JavaScript做到这一点的一种方法是使用几个句子,然后将它们与一个+运算符连接起来(我知道,这可能不是最有效的方法,但是我并不真正关心此阶段的性能,只是代码可读性) 。例:

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

我尝试在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 = ' + account_id + ' AND'\
    'record_def.account_id=' + account_id + ' AND'\
    'def_id=' + def_id

207
由于您的示例看起来像一个SQL块,仅在等待注入攻击,因此,另一个建议是考虑使用更高级别的SQL库(例如SQLAlchemy),或者避免类似的事情将原始SQL一起黑客入侵。(也许是题外话,但您确实要求“任何建议”。;)
约翰·盖恩斯(John Gaines Jr

6
这是“ 为长字符串创建多行代码的 Python方法”要创建包含换行符的字符串请参见textwrap.dedent
鲍勃·斯坦

8
@cezar我五年多以前就写了这个问题,但是我记得它起因于不知道如何正确地将长sql查询放在几行中。我同意我用那个长字符串做愚蠢的事情,但这不是我的问题,我也不聪明,无法寻找一个更好的示例来说明它不包含某些sql注入问题。
Pablo Mescher

@cezar不,这不是XY问题,在任何情况下,查询都最好以多行格式设置。SQLi与当前问题无关。但是,大胆的警告是完全有道理的:)
bugmenot123 '19

我为此写了一个小包装。此处的示例:stackoverflow.com/a/56940938/1842491
Shay

Answers:


2224

您在谈论多行字符串吗?容易,使用三引号将它们开始和结束。

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

您也可以使用单引号(当然在开始和结束时使用三个引号),并将结果字符串s与其他任何字符串一样对待。

注意:与任何字符串一样,引号和结尾引号之间的任何内容都将成为字符串的一部分,因此本示例中有一个前导空格(如@ root45所指出)。该字符串还将包含空格和换行符。

即:

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

最后,还可以像这样在Python中构造长行:

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

其中将包含任何额外的空格或换行符(这是一个有意的示例,显示了跳过空格的结果将导致什么):

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

不需要逗号,只需将要连接的字符串放在一对括号中,并确保考虑到任何需要的空格和换行符。


8
我更喜欢为第二种方法显式使用“ +”运算符。没有太多麻烦,并提高了可读性。
Marco Sulla 2014年

38
@LucasMalor相邻的字符串是编译时串联。是否不使用+运算符使连接在运行时发生?
约书亚·泰勒

13
作为参考,这里是这一现象的官方文档:docs.python.org/2/reference/...(蟒蛇2)和docs.python.org/3/reference/...(蟒蛇3)
neverendingqs

4
您的示例很好,但是我希望它包括演示如何安全可靠地将变量数据嵌入查询中。OP和@jessee示例代码都显示了如何不正确地进行操作(它们是对SQL攻击的邀请)。参见:dev.mysql.com/doc/connector-python/en/...
斯科特Prive的

2
您可以textwrap.dedent用来删除不需要的前导空格。docs.python.org/3/library/textwrap.html#textwrap.dedent
Corey Goldberg,

190

如果您不希望使用多行字符串,而只需要一个长的单行字符串,则可以使用括号,只需确保在字符串段之间不包含逗号,那么它将是一个元组。

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)

在您正在构造的SQL语句中,多行字符串也可以。但是,如果多行字符串将包含额外的空格将是一个问题,那么这将是实现所需内容的好方法。


1
@Pablo,您甚至可以在,
Ashwini Chaudhary

@ 200OK之后你的意思是'
kon psych 2014年

3
格式化此字符串的另一种方法是.format(...)在右括号后添加。%格式化符号也必须起作用,但我还没有尝试过
kon psych 2014年

3
请注意,每行必须以字符串常量结尾,因此' foo '+variable将不起作用,但' foo '+variable+''可以。
yoyo

46
此示例是打开SQL注入攻击的门户。请不要在任何面向公众的应用程序上使用此功能。请参阅如何使用“占位符” MySQL的文档:dev.mysql.com/doc/connector-python/en/...
斯科特私法

138

打破行\对我的作品。这是一个例子:

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

9
我希望使用三引号或将()
括起来

14
我强烈建议将空格放在以下各行的开头,而不是后面各行的结尾。通过这种方式,意外丢失的方式更加明显(因此不太可能发生)。
Alfe

也可以在行尾使用变量longStr = "account: " + account_id + \ ...
frmbelz 19/12/5

我收到以下错误消息:the backslash is redundant between brackets当我在内部写信时print()
alper

50

我发现自己对此很满意:

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

30
不同意。如果第一行(“ string = ...”)严重缩进怎么办?必须将以下几行的代码缩进零缩进,否则在缩进的块中间看起来很难看。
xjcl

1
好吧,我大部分冗长的字符串都出现在模块级别,非常适合。对于您而言,这显然不是最佳解决方案。
Eero Aaltonen

1
我喜欢这种方法,因为它具有阅读的特权。如果我们的字符串很长,那就没办法了……根据缩进的程度,您仍然可以限制每行80个字符。我认为python样式指南仍然很模糊。谢谢!
爱德华多·卢西奥

如果在模块下使用,这将非常丑陋,我也必须.replace('\t','')
alper

44

我发现在构建长字符串时,通常会执行诸如构建SQL查询之类的事情,在这种情况下,这是最好的:

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

莱文的建议是好的,但可能容易出错:

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

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

8
+1使得代码审阅者不必费心检查每一行的右端是否有空白。如@KarolyHorvath所指出,OP多次犯此错误。
鲍勃·斯坦

2
查看以类似方式编码的多行字符串时,我要求每行的左端都有足够的空格,以便于确认。
2016年

3
@ BobStein-VisiBone代码审查不应涉及语法错误或此类小错误,而应涉及实质。如果有人要提交具有语法错误的代码进行审查(因此根本无法运行或在某些情况下无法运行),则表示存在严重错误。在提交之前运行皮棉并不难。如果这个人没有注意到他们犯了这样一个明显的错误,那么他们的程序就无法正确运行,那么他们就不应该提交。
查尔斯·亚迪斯

1
同意@CharlesAddis,代码审查应该在自动化方法之后进行,例如,lint,语法突出显示等。但是,某些空白缺失的bug可能不会被这种方式捕获。我建议您在防御错误时应充分利用所有合理的优势。
鲍勃·斯坦

33

您还可以在使用“”符号时串联变量:

foo = '1234'

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

编辑:找到了一种更好的方法,命名为params和.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

此方法使用:

  • 只需一个反斜杠即可避免初始换行
  • 通过使用三引号引起来的字符串,几乎没有内部标点符号
  • 使用textwrap inspect模块去除局部缩进
  • account_iddef_id变量使用python 3.6格式的字符串插值('f')。

这种方式对我来说似乎是最pythonic的。

# 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}'''
)

更新:1/29/2019合并@ShadowRanger的建议使用inspect.cleandoc代替textwrap.dedent


5
注意:比inspect.cleandoc稍好一点textwrap.dedent,因为它不需要第一行为空,并在末尾添加换行符。
ShadowRanger

2
@ShadowRanger哇,我以前从未使用过cleandoc。我更新了答案,并将在以后的使用中使用inspect.cleandoc
Christopher Bruns

23

在Python> = 3.6中,您可以使用格式化字符串文字(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}'''

4
如果我想记录多行字符串的结果并且不显示左侧的制表符/空格,f字符串将如何工作?
kuanb

7
仍然容易受到SQL注入的攻击
Trenton '18

19

例如:

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'

如果condition的值应该是字符串,则可以这样:

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'"

13

textwrap.dedent这里找到了长字符串的最佳选择:

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

1
我喜欢防止自动换行的黑色斜线,非常感谢!
zyy

12

其他人已经提到了括号方法,但是我想在括号中添加,允许内联注释。

对每个片段进行评论:

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.
)

继续后不允许发表评论:

当使用反斜杠连续行(\)时,不允许注释。您会收到一个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

对Regex字符串的更好注释:

根据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)

10

我个人发现以下是用Python编写原始SQL查询的最佳方式(简单,安全和Pythonic),尤其是在使用Python的sqlite3模块时

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

优点

  • 简洁的代码(Pythonic!)
  • 防止SQL注入
  • 与Python 2和Python 3兼容(毕竟是Pythonic)
  • 无需字符串连接
  • 无需确保每行的最右字符是一个空格

缺点

  • 由于查询中的变量已被?占位符替换,因此?当查询中有很多变量时,要跟踪哪个变量将被哪个Python变量替换可能会有些困难。

请注意,我还没有对此进行测试,但是您可以通过在相关位置将其替换为“ {0} {1} {2}”,然后将最后一行更改为来避免问号混淆cursor.execute(query.format(vars))。那应该照顾你唯一的“骗子”(我希望)。

是的,使用format会很好,但是我不确定以这种方式格式化的查询字符串是否可以安全地用于SQL注入。
Faheel

是的,这是一个公平的观点,当然可能会有些棘手。也许在完全消耗的东西上进行测试是明智的……毫无疑问,这是一个比较。科学 本科生很快就会过去。;)

2
@Ben如果您这样做,cursor.execute(query.format(vars))您将不再能从准备好的语句中获利,因此您很容易遇到许多类型的问题,首先要注意的是,如果参数不仅仅是数字,则需要在SQL查询中双引号。
Patrick Mevzek '18年

4

我通常使用这样的东西:

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

如果要删除每行中令人讨厌的空格,可以执行以下操作:

text = '\n'.join(line.lstrip() for line in text.splitlines())

2
查看textwrap.dedent标准库中的Python 函数,它具有您需要的功能。
bjd2385 '18

@ bjd2385:inspect.cleandoc稍微好一点(关于文本是否与打开的引号出现在同一行上的要求不太严格,不需要显式的行继续符)。
ShadowRanger

3

您的实际代码不起作用,在“行”末尾缺少空格(例如: role.descr as roleFROM...

多行字符串有三引号:

string = """line
  line2
  line3"""

它将包含换行符和多余的空格,但是对于SQL来说这不是问题。


3

您还可以将sql语句放置在单独的文件中,action.sql然后使用以下命令将其加载到py文件中:

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

因此,sql语句将与python代码分开。如果sql语句中有需要从python填充的参数,则可以使用字符串格式(例如%s或{field})


3

“Àla” Scala方式(但是我认为这是OQ要求的最Python方式):

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('            | ',' ')

如果您想要没有跳线的最终str,只需将其放在\n第二个替换的第一个参数的开头:

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

注意:“ ...模板”之间的白线。和“还,...”在后面需要一个空格|


3

tl; dr:使用"""\"""包装字符串,如

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

官方python文档中

字符串文字可以跨越多行。一种方法是使用三引号:“”“ ...”“”或'''...'''。行尾会自动包含在字符串中,但是可以通过在行尾添加\来防止这种情况。下面的例子:

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

产生以下输出(请注意,不包括初始换行符):

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

2

嘿,尝试这种希望能起作用的方法,就像这种格式,它将像您已成功查询此属性一样,返回一条连续的行。

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

1

我使用递归函数来构建复杂的SQL查询。此技术通常可用于构建大型字符串,同时保持代码的可读性。

# 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)

PS:看一下很棒的python-sqlparse库,可以根据需要漂亮地打印SQL查询。 http://sqlparse.readthedocs.org/en/latest/api/#sqlparse.format


“递归函数”不是叫lambda吗?
m3nda 2015年

1

当代码(例如变量)缩进并且输出字符串应该是一个衬线(没有换行符)时,我认为另一种方法更易读:

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

使用三引号。人们经常在程序开始时使用它们来创建文档字符串,以解释其目的以及与该文档创建相关的其他信息。人们还在功能中使用这些来解释功能的目的和应用。例:

'''
Filename: practice.py
File creator: me
File purpose: explain triple quotes
'''


def example():
    """This prints a string that occupies multiple lines!!"""
    print("""
    This
    is 
    a multi-line
    string!
    """)

0

我喜欢这种方法,因为它具有阅读的特权。如果我们的弦长,那就没办法了!根据您所处的缩进级别,仍然限制为每行80个字符。。。嗯...无需赘述。我认为python样式指南仍然很模糊。我采用@Eero Aaltonen方法是因为它具有阅读和常识的特权。我知道样式指南应该对我们有帮助,而不会使我们的生活变得一团糟。谢谢!

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

官方python文档中

字符串文字可以跨越多行。一种方法是使用三引号:“”“ ...”“”或'''...'''。行尾会自动包含在字符串中,但是可以通过在行尾添加\来防止这种情况。下面的例子:

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

产生以下输出(请注意,不包括初始换行符):


0

为了在字典中定义一个长字符串, 保留换行符,但省略空格,我最终在一个常量中定义字符串,如下所示:

LONG_STRING = \
"""
This is a long sting
that contains newlines.
The newlines are important.
"""

my_dict = {
   'foo': 'bar',
   'string': LONG_STRING
}

0

作为Python中长字符串的一种通用方法,您可以使用三引号splitjoin

_str = ' '.join('''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.'''.split())

输出:

'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.'

关于OP的与SQL查询有关的问题,下面的答案无视此构建SQL查询方法的正确性,并且仅关注以可读性和美观性方式构建长字符串,而没有其他导入。它还忽略了这带来的计算负荷。

使用三重引号,我们构建了一个长且可读的字符串,然后使用split()将该字符串分解为一个列表,从而去除了空格,然后将其与重新连接在一起' '.join()。最后,我们使用以下format()命令插入变量:

account_id = 123
def_id = 321

_str = '''
    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 = {}
    '''

query = ' '.join(_str.split()).format(account_id, account_id, def_id)

产生:

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 = 123 AND record_def.account_id=123 AND def_id=321

编辑:这种方法不符合PEP8,但我有时发现它很有用


-7

通常,我将listjoin用于多行注释/字符串。

lines = list()
lines.append('SELECT action.enter code here descr as "action", ')
lines.append('role.id as role_id,')
lines.append('role.descr as role')
lines.append('FROM ')
lines.append('public.role_action_def,')
lines.append('public.role,')
lines.append('public.record_def, ')
lines.append('public.action')
query = " ".join(lines)

您可以使用任何字符串来连接所有此列表元素,例如' \n'(换行符)或' ,'(逗号)或' '(空格)

干杯..!!


1
您为什么至少不使用数组文字?
亚历山大-恢复莫妮卡

1
数组由类列表表示。查看有关数组字面量的另一讨论
paone

我想这行得通,但是您应该考虑性能和可读性...
Petro
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.