如何将长字符串的定义拆分成多行?

1955

我有一个非常长的查询语句,我想在Python中将其拆分成几行。在JavaScript中这样做的一种方法是使用几个句子并使用 + 运算符将它们连接起来(我知道,也许这不是最有效的方法,但在这个阶段我并不真正关心性能,只关心代码的易读性)。例如:

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

我曾试过在Python中做类似的事情,但它没有起作用,所以我使用了\来拆分长字符串。但是,我不确定这是否是唯一/最好/最符合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

346
由于您的示例看起来像是一段等待注入攻击的 SQL 代码块,另一个建议是考虑使用更高级别的 SQL 库,比如 SQLAlchemy 或者其他类似的库,以避免像这样拼凑原始 SQL 代码。 (可能有些离题,但是您确实要求“任何建议”。;) - John Gaines Jr.
11
这是“用Pythonic的方法创建多行代码以表示长字符串”的翻译。要创建一个包含换行符的字符串,请参阅textwrap.dedent - Bob Stein
10
@cezar 我写下这个问题已经超过五年了,但我记得它是由于不知道如何正确地将长的 SQL 查询分成几行而引起的。我同意我在那个长字符串中做了一些愚蠢的事情,但那不是我的问题,我也不够聪明去寻找一个更好的例子来说明它,而不包括一些 SQL 注入方面的问题。 - Pablo Mescher
@cezar 不,这不是一个 XY 问题,无论如何,查询最好以多行格式进行格式化。 SQLi 与问题无关。然而,大而粗的警告完全是合理的 :) - bugmenot123
显示剩余2条评论
30个回答

3145

你是在谈论多行字符串吗?很容易,使用三个引号来开始和结束它们。

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

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


20
我更喜欢在第二种方法中明确使用“+”运算符。这并不会很麻烦,而且可以提高可读性。 - Marco Sulla
81
相邻的字符串是在编译时拼接的。使用+操作符会导致拼接在运行时进行吗? - Joshua Taylor
34
以下是有关此现象的官方文档供参考:https://docs.python.org/2/reference/lexical_analysis.html#string-literal-concatenation(Python 2)和https://docs.python.org/3/reference/lexical_analysis.html#string-literal-concatenation(Python 3)。 - neverendingqs
4
你的示例很好,但我希望它能够演示如何安全地嵌入变量数据到查询中。OP和@jessee示例代码都展示了不正确的做法(它们容易引发SQL注入攻击)。参见:https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html - Scott Prive
8
你可以使用 textwrap.dedent 函数来去掉不需要的前导空格。https://docs.python.org/zh-cn/3/library/textwrap.html#textwrap.dedent - Corey Goldberg
显示剩余8条评论

317

如果您不想要一个多行字符串,而只是需要一个很长的单行字符串,您可以使用括号。只需确保在字符串段之间不包含逗号(否则将成为元组)。

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 语句中,多行字符串也可以使用。但如果多行字符串包含的额外空格会成为问题,则这将是实现所需的好方法。

正如评论中所指出的那样,以这种方式连接 SQL 查询存在 SQL 注入安全风险,因此请使用数据库的参数化查询功能来防止此类问题。但是,我不改变答案,因为它直接回答了所提出的问题。


2
@200OK 你的意思是在单引号后面吗? - kon psych
3
另一种格式化此字符串的方法是在闭合括号后添加 .format(...)% 格式化符号也应该可行,但我没有尝试过。 另一种格式化此字符串的方法是在右括号后添加 .format(...)% 格式化符号也可以使用,但我没有尝试过。 - kon psych
7
请注意每行必须以字符串常量结束,因此 ' foo '+variable 不起作用,但是 ' foo '+variable+'' 将起作用。 - yoyo
109
这个例子容易受到SQL注入攻击,请不要在任何公开的应用程序中使用它。请参考MySQL文档中有关如何使用“占位符”的说明:https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html - Scott Prive
1
@Crossfit_and_Beer 它们被称为查询参数,拥有它们的查询称为参数化查询。每个主要的关系型数据库管理系统都支持它们。 - jpmc26
显示剩余3条评论

188

使用\来打断行对我很有效。这里是一个例子:

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

11
我更喜欢使用三引号或者将内容包裹在圆括号中,而非使用反斜杠字符。 - Khanh Hua
39
我强烈建议将空格放在下一行的开头而不是上一行的结尾。这样,如果有遗漏,就更容易被发现(因此不太可能发生)。 - Alfe
1
也适用于行末变量 longStr = "账户:" + account_id + \ ... - frmbelz
3
@Alfe 不用再担心缺少反斜杠了。如果我漏掉一个,VSCode 就会心脏病发作。 - Arthur Tarasov
3
PEP 8Black都不鼓励使用反斜杠进行行续。 - Stevoisiak
显示剩余4条评论

64

我发现当构建长字符串时,通常是在构建 SQL 查询,此时最好的方法是:

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

Levon建议很好,但可能容易出错:

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

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

14
+1 可以减轻代码审查人员对每行代码结尾的不充分空格进行仔细检查的负担。正如@KarolyHorvath所指出的那样,OP犯了这个错误多次。 - Bob Stein
3
当我审核类似格式的多行字符串时,我要求每一行的左侧都有足够的空格以便于进行确认。 - Umbrella
4
代码审核不应该只关注语法错误或类似的小错误,而应该关注实质性问题。如果有人提交的代码存在语法错误(因此根本无法运行或在某些情况下无法运行),那么肯定出了严重问题。在提交代码之前运行 lint 并不难。如果这个人因为如此明显的错误没有注意到他们的程序无法正确运行,那么他们就不应该提交代码。 - Charles D Pantoga
1
同意@CharlesAddis的观点,代码审查应该在自动化方法之后进行,例如lint、语法高亮等。然而,有些缺少空格的错误可能无法通过这种方式捕获。我建议您充分利用所有合理的优势来防范错误。 - Bob Stein
另一个好处是您可以快速注释掉代码行。 - Ideogram

63

我发现自己对这个感到高兴:

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

56
不同意。如果第一行("string = ...")缩进很深,那么必须将后面的行去除缩进,这样看起来会破坏整个已经缩进的块的美观性。 - xjcl
2
嗯,在我的代码中,大多数长字符串出现在模块级别,这很适合。但在你的情况下,这显然不是最好的解决方案。 - Eero Aaltonen
1
我喜欢这种方法,因为它更注重阅读。在我们有很长的字符串的情况下,没有其他办法...取决于您所处的缩进级别,每行仍限制在80个字符...好吧...不需要再说什么了。在我看来,Python风格指南仍然非常模糊。谢谢! - Eduardo Lucio
1
如果在一个模块下使用这段代码会很丑陋,所以我必须添加.replace('\t','') - alper
2
如果您关心代码折叠,这将在大多数编辑器中破坏它。 - Keto

58

这种方法使用:

  • 三引号字符串几乎不使用内部标点符号
  • 使用inspect模块剥离局部缩进
  • 对于account_iddef_id变量,使用Python 3.6格式化字符串插值('f')

我认为这是最具Python风格的方式。

import inspect

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

8
注意:inspect.cleandoctextwrap.dedent更好,因为它不需要第一行为空,并且在末尾有一个行继续字符。 - ShadowRanger
3
哇,我以前从未使用过cleandoc。我更新了我的答案,并将来会使用inspect.cleandoc - Christopher Bruns
1
这对我非常有效!消除了编辑器在'''引用过程中添加的空格! - Joseph Astrahan
5
虽然看起来很不错,但我认为这种方法会容易受到SQL注入攻击。不幸的是,f-string在处理SQL查询方面效果不佳。从其他评论中可以得知,最好使用cursor.execute代替。具体请参考 https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html。 - jackblk

41

当使用 """ 符号时,您可以包含变量:

foo = '1234'

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

更好的方法是使用命名参数和.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)

2
在这里使用f字符串似乎更自然和简单。 - Timo
1
如果 {link}{name} 被使用了多次,我更喜欢使用 .format();但是如果变量名很长,我也更喜欢使用 .format() - Yzmir Ramirez

30

在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}'''

9
如果我想记录多行字符串的结果并且不显示左侧的制表符/空格,该如何使用 f-string? - kuanb
17
仍然容易受到 SQL 注入攻击。 - Trenton

25

PEP 8风格指南建议使用括号:

将长行代码进行换行的首选方式是在圆括号、方括号和花括号内使用Python隐式换行。通过在括号中包含表达式,可以在多行上分割长行。与使用反斜杠进行换行相比,应优先使用这些方法。

示例:

long_string = (
    "This is a lengthy string that takes up a lot of space. I am going to "
    "keep on typing words to fill up more and more space to show how you can "
    "split the string across multiple lines."
)

25

我发现在这里描述的情况下,textwrap.dedent 对于长字符串是最好的选择,详情可参考这里

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

4
我喜欢这个防止自动换行的黑色斜线,非常感谢! - zyy
3
如果你使用inspect.cleandoc而不是textwrap.dedent,那么反斜杠就不必要了。 - Christopher Bruns
2
这个被低估了,简直是犯罪。 - chepner
在我看来,这可能是这里最好的解决方案之一,感谢您揭示了 Python 标准库中另一个不错的部分。 - Brent Pappas
那就是我一直在寻找的答案。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接