带参数的多行字符串。如何声明?

110

假设我有一个非常长的字符串和我想要创建的参数。我知道你可以使用

cmd = """line 1
      line 2
      line 3"""

但是现在假设我想把1、2和3作为参数传递。

这个可以实现。

cmd = """line %d
      line %d
      line %d""" % (1, 2, 3)

但是,如果我有一个包含30多个参数的超长字符串,我该如何在多行中传递这些参数?在单行中传递它们甚至会使创建多行字符串的尝试失去意义。

提前感谢任何人的帮助和见解。

10个回答

118

您可以使用str.format()函数,该函数允许使用命名参数,因此:

'''line {0}
line {1}
line {2}'''.format(1,2,3)

你当然可以使用Python的*args语法来扩展这个函数,以允许你传入一个tuplelist:

args = (1,2,3)
'''line {0}
line {1}
line {2}'''.format(*args)

如果您能智能地为参数命名,最稳健的解决方案(尽管是最需要输入的解决方案)将是使用 Python 的 **kwargs 语法传递一个字典:

args = {'arg1':1, 'arg2':2, 'arg3':3}
'''line {arg1}
line {arg2}
line {arg3}'''.format(**args)

想要了解更多关于str.format()小语言的信息,请到这里查看。


1
+1 **kwargs 格式在许多情况下都非常易读(尽管可能不适用于 OP 的示例)。 - Casey Kuball
'''string here''' 技术是一种可靠的技术,而且效果很好,但当您不得不将除第一行以外的所有内容左对齐并且源代码缩进了几个级别时,它会创建一些非常丑陋的源代码。为了获得漂亮且易于查看的源代码和良好的对齐方式,请改用我在这里提供的技术:https://dev59.com/t2gu5IYBdhLWcg3w6bKo#54564926。当然,它也有其缺点,但对于少于一个长段落的文本,我更喜欢使用这种技术而不是使用 ''' ''' 技术。 - Gabriel Staples
请看这个自我记录的文档字符串打印,作为模块文档,它使用了您上面介绍的技术类型。我刚刚将其添加到我的答案底部:https://dev59.com/t2gu5IYBdhLWcg3w6bKo#54564926。 - Gabriel Staples
2
如果你的字符串中有花括号,可以通过使用双花括号进行转义:{{ 和 }}。 - LoMaPh

63

您可以利用括号(和逗号,的续行属性。

cmd = """line %d
      line %d
      line %d""" % (
      1,
      2,
      3)

1
非常感谢。正是我所需要的。以前不知道在使用括号时可以有多行。这对我帮助很大!!! - Quillion

55

最简单的方法可能是使用字面字符串插值(从Python 3.6开始可用,并假设所有参数都在作用域内)。

cmd = f"""line {1}
      line {2}
      line {3}"""

1
谢谢,但是在原始字符串包含许多括号的情况下,这并不适用。 - Luk Aron
2
为什么这不是最好的方式?它更易读,更容易编写。 - Akshay
1
@LukAron 你可以在f-string中写{{来得到一个"{",我认为这不会降低可读性,在非极端情况下(其他解决方案可能也同样有问题)。 - Neinstein

20

使用string.format()函数的另一种变体。

s = "{0} " \
    "{1} " \
    "{2}" \
    .format("Hello", "world", "from a multiline string")    
print(s)

15

摘要

直接跳到下面的示例1和示例4。

完整答案:

我刚刚了解了Python的textwrap模块,它有一个非常方便的textwrap.dedent()函数,并且考虑到自Python 2.7以来一直存在,我无法相信它不更受欢迎!

在多行字符串周围使用textwrap.dedent()可以解决所有我之前回答中的问题

这是官方文档(重点标出)

textwrap.dedent(text)

Remove any common leading whitespace from every line in text.

This can be used to make triple-quoted strings line up with the left edge of the display, while still presenting them in the source code in indented form.

Note that tabs and spaces are both treated as whitespace, but they are not equal: the lines " hello" and "\thello" are considered to have no common leading whitespace.

Lines containing only whitespace are ignored in the input and normalized to a single newline character in the output.

For example:

def test():
    # end first line with \ to avoid the empty line!
    s = '''\
    hello
      world
    '''
    print(repr(s))          # prints '    hello\n      world\n    '
    print(repr(dedent(s)))  # prints 'hello\n  world\n'

对于所有示例

import textwrap

例子 1

所以,不要像这样做,如最受欢迎的答案所述(这会导致缩进不整齐):

cmd = '''line {0}
line {1}
line {2}'''.format(1,2,3)

print(cmd)

这样做(并保持漂亮、整洁的缩进)!

import textwrap

cmd = textwrap.dedent('''\
    line {0}
    line {1}
    line {2}''').format(1,2,3)

print(cmd)

或者,使用Python3的新版改良型“f”格式字符串,而不是.format()方法!:

import textwrap

var0 = 1
var1 = 2
var2 = 3
cmd = textwrap.dedent(f'''\
    line {var0}
    line {var1}
    line {var2}''')

print(cmd)

例子 2

如果你有很多参数需要传递给 format() 函数,你可以将它们分成多行写,如果需要的话。注意这里的 format() 参数占用了两行:

cmd = textwrap.dedent('''\
    line {0}
    line {1}
    line {2}
    line {3}
    line {4}
    line {5}
    line {6}
    line {7}
    line {8}
    line {9}
    line {10}
    line {11}
    line {12}
    line {13}
    line {14}
    line {15}
    line {16}
    line {17}
    line {18}
    line {19}''').format(
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
        11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
    )

print(cmd)

当然,如果 format() 的参数非常长,您也可以将每个参数放在自己的一行中:

cmd = textwrap.dedent('''\
    line {0}
    line {1}
    line {2}
    line {3}
    line {4}
    line {5}
    line {6}
    line {7}
    line {8}
    line {9}
    line {10}
    line {11}
    line {12}
    line {13}
    line {14}
    line {15}
    line {16}
    line {17}
    line {18}
    line {19}''').format(
        100000000000000000000000000000000000000000000000000000000000000000001,
        100000000000000000000000000000000000000000000000000000000000000000002,
        100000000000000000000000000000000000000000000000000000000000000000003,
        100000000000000000000000000000000000000000000000000000000000000000004,
        100000000000000000000000000000000000000000000000000000000000000000005,
        100000000000000000000000000000000000000000000000000000000000000000006,
        100000000000000000000000000000000000000000000000000000000000000000007,
        100000000000000000000000000000000000000000000000000000000000000000008,
        100000000000000000000000000000000000000000000000000000000000000000009,
        100000000000000000000000000000000000000000000000000000000000000000010,
        100000000000000000000000000000000000000000000000000000000000000000011,
        100000000000000000000000000000000000000000000000000000000000000000012,
        100000000000000000000000000000000000000000000000000000000000000000013,
        100000000000000000000000000000000000000000000000000000000000000000014,
        100000000000000000000000000000000000000000000000000000000000000000015,
        100000000000000000000000000000000000000000000000000000000000000000016,
        100000000000000000000000000000000000000000000000000000000000000000017,
        100000000000000000000000000000000000000000000000000000000000000000018,
        100000000000000000000000000000000000000000000000000000000000000000019,
        100000000000000000000000000000000000000000000000000000000000000000020,
    )

print(cmd)

示例3

与此不同的是如我在原回答中所述(该方法保持了良好的缩进格式,但使用起来有些繁琐):

print("\n\n" + 
      "########################\n" + 
      "PRINT DOCSTRING DEMO:\n" + 
      "########################")

现在你可以这样做!--这允许我的多行字符串在打印时与显示的左边缘对齐,同时仍以缩进形式在源代码中呈现(请参见官方文档):

# Note: use the `\` below to prevent the implicit newline right after it from being printed.
print(textwrap.dedent("""

      ########################
      PRINT DOCSTRING DEMO:
      ########################\
      """))

例子4

相比于这种有一些丑陋的缺少缩进的东西,可以考虑使用下面的替代方法:

def printDocstrings1():
    """
    Print all document strings for this module, then exit.
    Params:  NA
    Returns: NA
    """

    # A LITTLE BIT UGLY, BUT IT WORKS.
    print("""
---------------------
Module Documentation:
---------------------
printDocstrings:{}
myFunc1:{}
class Math:{}
    __init__:{}
    add:{}
    subtract:{}""".format(
        printDocstrings1.__doc__,
        myFunc1.__doc__,
        Math.__doc__,
        Math.__init__.__doc__,
        Math.add.__doc__,
        Math.subtract.__doc__))

...做这个,它使用 textwrap.dedent() 来保持整个缩进看起来漂亮!

def printDocstrings2():
    """
    Print all document strings for this module, then exit.
    Params:  NA
    Returns: NA
    """

    # MUCH CLEANER! Now I can have the proper indentation on the left withOUT
    # it printing that indentation!
    print(textwrap.dedent("""\
    ---------------------
    Module Documentation:
    ---------------------
    printDocstrings:{}
    myFunc1:{}
    class Math:{}
        __init__:{}
        add:{}
        subtract:{}""").format(
            printDocstrings2.__doc__,
            myFunc1.__doc__,
            Math.__doc__,
            Math.__init__.__doc__,
            Math.add.__doc__,
            Math.subtract.__doc__))

运行以上代码

你可以在我的eRCaGuy_hello_world GitHub存储库中运行我上面的测试代码,链接: textwrap_practice_1.py.

运行命令:

./textwrap_practice_1.py

或:

python3 textwrap_practice_1.py

6

2020年10月19日更新:虽然我在这里的回答仍然有价值、信息量大,值得阅读,但我现在有更好的答案,依赖于非常有用的textwrap.dedent()函数。


@Chinmay Kanchi所说,你可以这样做:

'''line {0}
line {1}
line {2}'''.format(1,2,3)

然而,我认为新行的对齐方式必须是完全左对齐,尤其是当你已经进行了多级缩进时,这样看起来很傻,因此我更喜欢写成这样:

'''line {0}
   line {1}
   line {2}'''.format(1,2,3)

这样做是可以的,但是是错误的!它将 line {1}line {2} 左侧的所有空格都解释为真正的空格,因此打印出来会看起来很奇怪:

1
   2
   3

与其

1
2
3

所以,一个解决办法是使用+运算符连接字符串,并在连接的字符串周围加上括号,以及显式的换行符(\n),像这样:
('line {0}\n' + 
 'line {1}\n' +
 'line {2}').format(1,2,3)

在我看来,完美!现在如果您打印它,它的源代码和实际字符串都看起来很好并且对齐。

完整示例:

丑陋的源代码!

num1 = 7
num2 = 100
num3 = 75.49

# Get some levels of indentation to really show the effect well.
# THIS IS *UGLY*! Notice the weird forced-left-align thing for the string I want to print!
if (True):
    if (True):
        if (True):
            # AAAAAH! This is hard to look at!
            print('''num1 = {}
num2 = {}
num3 = {}'''.format(num1, num2, num3))

            # More lines of code go here
            # etc
            # etc

输出:

num1 = 7
num2 = 100
num3 = 75.49

漂亮的例子!在源代码中看起来很棒。:)

这是我喜欢的。

# Get some levels of indentation to really show the effect well.
if (True):
    if (True):
        if (True):
            # IMPORTANT: the extra set of parenthesis to tie all of the concatenated strings together here is *required*!
            print(('num1 = {}\n' + 
                   'num2 = {}\n' + 
                   'num3 = {}')
                   .format(num1, num2, num3))

            # More lines of code go here
            # etc
            # etc

输出:

num1 = 7
num2 = 100
num3 = 75.49

2019年5月21日更新:有时候“丑陋”的多行字符串确实是最好的选择!

因此,我一直在使用Python从基于文本的配置文件中自动生成C头文件和源文件(.h/.c),经过大量尝试后,我得出结论:将大块文本从配置文件复制粘贴到Python脚本中带来的好处胜过任何“丑陋”因素。

因此,当需要大量多行复制粘贴的字符串时,我确定以下是我的首选方式,例如:

选项1:

  • Use parenthesis around the whole long string to allow the opening """ to be on a new line

    Get some levels of indentation to still show the "ugliness" effect.

    if (True): if (True): if (True): header = ( """ /* my custom file header info here */

    #pragma once

    #include "{}"

    const {} {}; """).format(include, struct_t, struct)

              print("header =" + header)
    

选项2:

  • No parenthesis, but still put the closing """ on its own line

    Get some levels of indentation to still show the "ugliness" effect.

    if (True): if (True): if (True): header = """ /* my custom file header info here */

    #pragma once

    #include "{}"

    const {} {}; """.format(include, struct_t, struct)

              print("header =" + header)
    
选项三:
  • No parenthesis around the whole string, and put the closing """ on the same line as the string contents to prevent adding a (potentially undesirable) \n at the end.

  • However, put the remainder of format( on a new line (or on many new lines) in case it is long.

    Get some levels of indentation to still show the "ugliness" effect.

    if (True): if (True): if (True): header = """ /* my custom file header info here */

    #pragma once

    #include "{}"

    const {} {};""".format( include, struct_t, struct) # indentation here can literally be anything, but I like to indent 1 level; since it's inside parenthesis, however, it doesn't matter

              print("header =" + header)
    

输出:

  • 选项1和2产生完全相同的输出,字符串末尾有一个额外的\n,在大多数情况下是可以接受的。
  • 选项3产生与选项1和2完全相同的输出,但是它的字符串末尾没有额外的\n,如果这对你的情况不可取,则可以选择此选项。
  • 无论您使用选项1、2还是3,都不会有太大区别——这只是用户偏好的问题,除了上面提到的额外的\n

以下是选项1、2和3打印的内容:

/*
my custom file header info here
*/

#pragma once

#include "<stdint.h>"

const my_struct_t my_struct;


将“美丽”和“丑陋”方法结合起来,在本演示中获得最佳结果:打印您的模块的docstring文档!

以下是一个基本示例,展示了如何同时使用上述“美丽”和“丑陋”的多行字符串方法,以便获得各自的最佳效果。这还展示了如何使用和打印模块的“docstrings”来记录您的模块。请注意,基于"""的多行技术为我们提供了很好的间距,因为我在下面的方式中完成了它,即在开头的"""之后和结束的"""之前有一个自动换行符(\n),因为这就是字符串的写法。

# PRETTY, AND GOOD.
print("\n\n" + 
      "########################\n" + 
      "PRINT DOCSTRING DEMO:\n" + 
      "########################")

import sys

def printDocstrings():
    """
    Print all document strings for this module, then exit.
    Params:  NA
    Returns: NA
    """

    # A LITTLE BIT UGLY, BUT GOOD! THIS WORKS GREAT HERE!
    print("""
---------------------
Module Documentation:
---------------------
printDocstrings:{}
myFunc1:{}
class Math:{}
    __init__:{}
    add:{}
    subtract:{}""".format(
        printDocstrings.__doc__,
        myFunc1.__doc__,
        Math.__doc__,
        Math.__init__.__doc__,
        Math.add.__doc__,
        Math.subtract.__doc__))

    sys.exit()

def myFunc1():
    """
    Do something.
    Params:  NA
    Returns: NA
    """
    pass

class Math:
    """
    A basic "math" class to add and subtract
    """

    def __init__(self):
        """
        New object initialization function.
        Params:  NA
        Returns: NA
        """
        pass

    def add(a, b):
        """
        Add a and b together.
        Params:  a   1st number to add
                 b   2nd number to add
        Returns: the sum of a + b
        """
        return a + b

    def subtract(a, b):
        """
        Subtract b from a.
        Params:  a   number to subtract from
                 b   number to subtract
        Returns: the result of a - b
        """
        return a - b

printDocstrings() 

输出:
- 注意到它的漂亮和良好格式化了吗?当你以这种方式打印它们时,你的文档字符串的制表符、换行和空格都被自动保留了!

  
########################  
PRINT DOCSTRING DEMO:  
########################  
  
---------------------  
Module Documentation:  
---------------------  
printDocstrings:  
    Print all document strings for this module, then exit.  
    Params:  NA  
    Returns: NA  
      
myFunc1:  
    Do something.  
    Params:  NA  
    Returns: NA  
      
class Math:  
    A basic "math" class to add and subtract  
      
    __init__:  
        New object initialization function.  
        Params:  NA  
        Returns: NA  
          
    add:  
        Add a and b together.  
        Params:  a   1st number to add  
                 b   2nd number to add  
        Returns: the sum of a + b  
          
    subtract:  
        Subtract b from a.  
        Params:  a   number to subtract from  
                 b   number to subtract  
        Returns: the result of a - b  
          
  

参考文献:

  1. Python文档字符串: https://www.geeksforgeeks.org/python-docstrings/
  • Note: you can also use the help() method to access a module or class's documentation (but in an interactive manner), as shown in the link above, like this:

         help(Math)  # to interactively display Class docstring
         help(Math.add)  # to interactively display method's docstring 
    

5
为了使参数在插入它们的同一行,您可以像这样操作:

cmd = "line %d\n"%1 +\
      "line %d\n"%2 +\
      "line %d\n"%3

[编辑:]针对第一条评论,我想到了以下内容:
cmd = "\n".join([
      "line %d"%1,
      "line %d"%2,
      "line %d"%3])

使用30多个参数来完成这个任务并不是非常高效的做法。另一方面,对于一个列表来说,使用"".join()则会更加高效。 - Frédéric Hamidi

3
您可以使用textwrap.dedent来删除行首空格:
import textwrap

cmd = str.strip(textwrap.dedent(
    '''
        line {}
            line with indent
        line {}
        line {}
    '''
    .format(1, 2, 3)))

这将导致:
line 1
    line with indent
line 2
line 3

请投票,使用PyCharm的人: https://youtrack.jetbrains.com/issue/PY-34646。自动格式化所做的事情以及问题中代码段的格式检查规则是不一致的。 - George Sovetov

2
这对我有效:
cmd = """line %d
      line %d
      line %d""" % (
          1,
          2,
          3
      )

2
这是最简单的版本,也方便IDE检查format参数:

这里是最简单的版本,还可以方便地检查format参数的IDE友好版:

cmd = (
    'line {}\n'
    'line {}\n'
    'line {}\n'
    .format(1, 2, 3))

多行参数版本:

cmd = (
    'line {}\n'
    'line {}\n'
    'line {}\n'
    .format(
        'very very very very very very very very very long 1',
        'very very very very very very very very very long 2',
        'very very very very very very very very very long 3',
    )
)

请投票,使用PyCharm的人:https://youtrack.jetbrains.com/issue/PY-34646。问题中的代码片段中自动格式化和格式检查不一致。 - George Sovetov

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