python多行字符串的正确缩进

Proper indentation for Python multiline strings

函数中python多行字符串的正确缩进是什么?

1
2
3
4
    def method():
        string ="""line one
line two
line three"""

1
2
3
4
    def method():
        string ="""line one
        line two
        line three"""

或者别的什么?

在第一个示例中,字符串挂在函数外部看起来有点奇怪。


你可能想和"""排成一队

1
2
3
4
def foo():
    string ="""line one
             line two
             line three"""

由于换行符和空格包含在字符串本身中,因此必须对其进行后处理。如果您不想这样做,并且有大量的文本,那么您可能需要将其单独存储在一个文本文件中。如果一个文本文件对您的应用程序不起作用,并且您不想进行后处理,我可能会选择

1
2
3
4
def foo():
    string = ("this is an"
             "implicitly joined"
             "string")

如果要对多行字符串进行后处理,以消除不需要的部分,则应考虑textwrap模块或PEP 257中介绍的后处理docstrings的技术:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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 '
'
.join(trimmed)


textwrap.dedent函数允许您从源文件中正确的缩进开始,然后在使用前将其从文本中删除。

正如其他一些人所指出的,权衡是,这是对文本的额外函数调用;在决定将这些文本放在代码中的位置时,要考虑到这一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)

日志消息文本中的尾随\用于确保换行符不在文本中;这样,文本就不会以空行开头,而是以下一整行开头。

EDOCX1的返回值(0)是输入字符串,在该字符串的每一行上删除所有常见的前导空格缩进。因此,上述log_message值为:

1
2
3
4
Prepare to frobnicate:
Here it comes...
    Any moment now.
And: Frobnicate!


其他答案中似乎缺少的一个选项(仅在NAXA的评论中深入提到)是:

1
2
3
4
5
6
7
8
9
def foo():
    string = ("line one
"
         # Add
 in the string
             "line two" "
"
     # Add"
" after the string
             "
line three
")

这将允许适当的对齐,隐式连接行,并且仍然保持行移动,对我来说,这是我为什么无论如何都要使用多行字符串的原因之一。

它不需要任何后处理,但您需要在希望行结束的任何给定位置手动添加
。可以是内联的,也可以是后面的单独字符串。后者更容易复制粘贴。


使用inspect.cleandoc如下:

1
2
3
4
5
def method():
    string = inspect.cleandoc("""
        line one
        line two
        line three"""
)

将按预期保持相对压痕。

Note: It's good practice to indent logical blocks of code under its related context to clarify the structure. E.g. the multi-line string belonging to the variable string.


还有一些选择。在启用了pylab的ipython中,dedent已经在命名空间中。我查过了,是从Matplotlib寄来的。也可以通过以下方式导入:

1
from matplotlib.cbook import dedent

在文档中,它声明它比textwarp等价的快,在我的ipython测试中,它比我的快速测试平均快3倍。它还有一个好处,那就是它丢弃了任何前导空白行,这使您能够灵活地构造字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
"""
line 1 of string
line 2 of string
"""


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


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

利用这三个例子中的matplotlib,将得到同样合理的结果。textwarp dedent函数将在第一个示例中有一个前导空白行。

明显的缺点是,文本包装在标准库中,而matplotlib是外部模块。

这里有些折衷…DEDENT函数使您的代码在定义字符串的地方更易于阅读,但需要稍后进行处理才能获得可用格式的字符串。在docstring中,很明显您应该使用正确的缩进,因为大多数docstring的使用都会进行所需的处理。

当我在代码中需要一个非长字符串时,我会发现下面的代码确实很难看,我让长字符串从封闭的缩进中退出。当然,在"美丽胜于丑陋"这一问题上失败了,但有人可能会说,它比"丑陋"的选择更简单、更明确。

1
2
3
4
5
6
7
8
9
10
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()

如果您想要一个快速简单的解决方案并避免自己键入新行,您可以选择一个列表,例如:

1
2
3
4
5
6
7
8
9
10
def func(*args, **kwargs):
    string = '
'
.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


我更喜欢

1
2
3
4
5
6
7
    def method():
        string = \
"""\
line one
line two
line three\
"""

1
2
3
4
5
6
    def method():
        string ="""\
line one
line two
line three\
"""


我的两分钱,从行尾逃出去拿缩进:

1
2
3
4
5
6
7
def foo():
    return"{}
"
\
          "freq: {}
"
\
          "temp: {}
"
.format( time, freq, temp )

第一个选项是好的-包括缩进。它是Python风格的-为代码提供可读性。

要正确显示:

1
print string.lstrip()


我来这里是想找一个简单的1行程序来删除/更正要打印的docstring的标识级别,而不会使它看起来不整洁,例如,通过在脚本中使其"挂起在函数之外"。

我最后做的是:

1
2
3
4
5
6
7
8
9
10
11
import string
def myfunction():

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


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

显然,如果使用空格(例如4)而不是制表键进行缩进,请使用类似这样的内容:

1
2
3
print str(string.replace(myfunction.__doc__,'
    '
,'
'
))[1:]

如果您希望DocStrings看起来像这样,则无需删除第一个字符:

1
2
3
4
5
6
7
   """line 1 of docstring
    line 2 of docstring
    line 3 of docstring"""


print string.replace(myfunction.__doc__,'
\t'
,'
'
)


对于字符串,您可以在处理完字符串之后进行处理。对于docstrings,您需要在处理完函数之后进行处理。这是一个仍然可读的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Lstrip(object):
    def __rsub__(self, other):
        import re
        return re.sub('^
'
, '', re.sub('
$'
, '', re.sub('
\s+'
, '
'
, 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__


这取决于文本的显示方式。如果您希望所有的代码都保持对齐,那么要么将其格式化为第一个代码段中的格式,要么遍历左侧的行,修剪所有的空间。


我有时会将多行文本作为字符串列表写入,然后将它们连接起来…

1
2
3
4
5
6
    part ="".join([
       "\x00\x00\x00\x00\x0C\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00"
       "\x00\x00\x00\x00\x0C\x00\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00",
       "\x00\x00\x00\x00\x0C\x00\x00\x00\x00\x00\xFF\x00\x00\x00\x00\x00",
       "\x00\x00\x00\x00\x0C\x00\x00\x00\x00\x00\x00\xFF\x00\x00\x00\x00",
    ])

这不是最有效的方法,但是对于大多数用例来说,它的性能已经足够好了,而且它不会干扰您的缩进,也不会要求第一行具有与第二行不同的缩进,就像三重引用方法一样。