ChatGPT解决这个技术问题 Extra ChatGPT

多行字符串的正确缩进?

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

    def method():
        string = """line one
line two
line three"""

或者

    def method():
        string = """line one
        line two
        line three"""

或者是其他东西?

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

处理文档字符串 specially:删除第一行的任何缩进;占据所有其他非空行的最小公共缩进将从它们中全部删除。除此之外,不幸的是,Python 中的多行字符串文字在空格方面是所见即所得:字符串分隔符之间的所有字符都成为字符串的一部分,包括缩进,Python 阅读本能,看起来它应该从文字开始的行的缩进开始测量。
@EvgeniSergeev 处理工具执行此任务(这在很大程度上取决于您选择的处理工具)。 method.__doc__ 不会像任何其他 str 文字那样被 Python 本身修改。

S
Salman von Abbas

您可能想与 """ 排队

def foo():
    string = """line one
             line two
             line three"""

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

def foo():
    string = ("this is an "
              "implicitly joined "
              "string")

如果您想对多行字符串进行后处理以删除不需要的部分,则应考虑 textwrap 模块或 PEP 257 中提供的用于后处理文档字符串的技术:

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 '\n'.join(trimmed)

这是续行的“悬挂缩进”样式。它在 PEP8 中被规定用于函数定义和长 if 语句等目的,但未针对多行字符串提及。就个人而言,这是我拒绝遵循 PEP8 的一个地方(而是使用 4 空格缩进),因为我非常不喜欢悬挂缩进,这对我来说模糊了程序的正确结构。
@buffer,在官方教程的 3.1.2 中(“两个相邻的字符串文字自动连接......”)和语言参考。
具有自动字符串连接的第二种形式不包括换行符这是一项功能。
PEP257 中指定的 trim() 函数在标准库中实现为 inspect.cleandoc
+1 @bobince 关于在此处拒绝“悬挂缩进”的评论...特别是因为如果您将变量名称从 string 更改为 text 或其他任何长度,那么您现在需要更新缩进实际上是多行字符串的每一行,只是为了让它与 """ 正确匹配。缩进策略不应该使未来的重构/维护复杂化,这是 PEP 真正失败的地方之一
b
bignose

textwrap.dedent 函数允许从源代码中的正确缩进开始,然后在使用前将其从文本中剥离。

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

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)

日志消息文字中的尾随 \ 是为了确保换行符不在文字中;这样,文字不会以空行开头,而是从下一个完整行开始。

textwrap.dedent 的返回值是输入字符串,在字符串的每一行中所有常见的前导空白缩进都已删除。所以上面的 log_message 值将是:

Prepare to frobnicate:
Here it comes...
    Any moment now.
And: Frobnicate!

虽然这是一个合理的解决方案并且很高兴知道,但在一个经常调用的函数中做这样的事情可能会被证明是一场灾难。
@haridsv 为什么那会是一场灾难?
@jtmoulia:比灾难更好的描述是“低效”,因为 textwrap.dedent() 调用的结果是一个常量值,就像它的输入参数一样。
@haridsv 灾难/低效率的根源是 定义 一个常量字符串 inside 一个经常调用的函数。可以将每次调用常量定义换成每次调用查找。这样,dedent 预处理将运行一次。一个相关的问题可能是 stackoverflow.com/q/15495376/611007 它列出了避免为每次调用定义常量的想法。尽管替代方案似乎需要查找。尽管如此,仍尝试了各种方法来找到存储它的有利位置。例如:def foo: return foo.x 然后下一行 foo.x = textwrap.dedent("bar")
我想如果该字符串用于仅在调试模式下启用的日志记录,并且在其他情况下未使用,那将是低效的。但是,为什么还要记录多行字符串文字呢?所以很难找到一个现实生活中的例子,上面的方法效率低下(即它大大减慢了程序的速度),因为任何消耗这些字符串的东西都会变慢。
w
wihlke

像这样使用 inspect.cleandoc

import inspect

def method():
    string = inspect.cleandoc("""
        line one
        line two
        line three""")

将按预期保持相对缩进。如下面的 commented,如果要保留前面的空行,请使用 textwrap.dedent。但是,这也保留了第一个换行符。

注意:最好在相关上下文下缩进逻辑代码块以阐明结构。例如属于变量字符串的多行字符串。


很困惑为什么这个答案直到现在才存在,inspect.cleandocPython 2.6 就已经存在,即 2008..?绝对是最干净的答案,特别是因为它不使用悬挂缩进样式,这只会浪费不必要的空间
此解决方案会删除前几行空白文本(如果有)。如果您不想要这种行为,请使用 textwrap.dedent docs.python.org/2/library/textwrap.html#textwrap.dedent
h
holroy

其他答案中似乎缺少的一个选项(仅在 naxa 的评论中深入提及)如下:

def foo():
    string = ("line one\n"          # Add \n in the string
              "line two"  "\n"      # Add "\n" after the string
              "line three\n")

这将允许正确对齐,隐式连接线,并且仍然保持线移位,对我来说,这是我无论如何都想使用多行字符串的原因之一。

它不需要任何后处理,但您需要在您希望该行结束的任何给定位置手动添加 \n。内联或作为单独的字符串之后。后者更容易复制粘贴。


请注意,这是一个隐式连接字符串的示例,而不是多行字符串。
@trk,它是多行的,因为字符串包含换行符(又名多行),但是是的,它使用连接来规避 OP 的格式问题。
这对我来说似乎是最好的答案。但是到目前为止,我不明白为什么 python 需要三引号运算符,如果它们导致难以阅读的代码。
J
Joop

还有一些选择。在启用了 pylab 的 Ipython 中,dedent 已经在命名空间中。我查了一下,它来自matplotlib。或者可以通过以下方式导入:

from matplotlib.cbook import dedent

在文档中,它指出它比等效的 textwrap 更快,并且在我在 ipython 中的测试中,我的快速测试确实平均快了 3 倍。它还具有丢弃任何前导空白行的好处,这使您可以灵活地构造字符串:

"""
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 dedent 将给出相同的合理结果。 textwrap dedent 函数将具有第一个示例的前导空行。

明显的缺点是 textwrap 在标准库中,而 matplotlib 是外部模块。

这里的一些权衡... dedent 函数使您的代码在定义字符串的地方更具可读性,但需要稍后进行处理以获取可用格式的字符串。在文档字符串中,很明显您应该使用正确的缩进,因为大多数文档字符串的使用都会进行所需的处理。

当我在我的代码中需要一个非长字符串时,我发现以下公认的丑陋代码,我让长字符串从封闭的缩进中退出。 “美丽胜于丑陋。”肯定失败了,但有人可能会争辩说,它比有齿的替代方案更简单、更明确。

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

s
steabert

如果您想要一个快速简便的解决方案并避免输入换行符,您可以选择一个列表,例如:

def func(*args, **kwargs):
    string = '\n'.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

虽然这不是最好的方法,但我不时使用它。如果确实使用它,则应该使用元组而不是列表,因为在加入之前不会对其进行修改。
l
lk_vc

我更喜欢

    def method():
        string = \
"""\
line one
line two
line three\
"""

或者

    def method():
        string = """\
line one
line two
line three\
"""

这并不能回答问题,因为问题明确指出缩进(在函数内)很重要。
@bignose问题说“它看起来有点奇怪”不允许使用。
如果没有丑陋的缩进,我将如何做到这一点?
@lfender6445 好吧,也许您可以将所有这些字符串与其他代码放在一个单独的文件中...
S
Simon

我的两分钱,逃脱行尾以获得缩进:

def foo():
    return "{}\n"\
           "freq: {}\n"\
           "temp: {}\n".format( time, freq, temp )

J
James Gowdy

我来这里是为了寻找一个简单的 1-liner 来删除/更正用于打印的文档字符串的标识级别,而不会使它看起来不整洁,例如通过使其在脚本中“挂在函数之外”。

这就是我最终做的事情:

import string
def myfunction():

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

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

显然,如果您使用空格(例如 4)而不是 tab 键缩进,请改用以下内容:

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

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

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

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

这在类方法和嵌套类上失败。
g
geckos

对于字符串,您可以在处理字符串之后。对于文档字符串,您需要改为在处理函数之后。这是一个仍然可读的解决方案。

class Lstrip(object):
    def __rsub__(self, other):
        import re
        return re.sub('^\n', '', re.sub('\n$', '', re.sub('\n\s+', '\n', 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__

处理文档字符串必须已经处理一致的缩进,如 described in PEP 257。已经有工具——例如inspect.cleandoc——以正确的方式做到这一点。
B
Bog Dia

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

要正确显示它:

print string.lstrip()

这似乎是格式化三引号字符串的最简单和最干净的方法,因此您不会因为缩进而有额外的空格
这只会删除多行字符串第一行中的前导空格。它对格式化以下行没有帮助。
I
Ignacio Vazquez-Abrams

这取决于您希望文本如何显示。如果您希望所有内容都左对齐,则可以将其格式化为第一个片段中的格式,或者遍历左修剪所有空间的行。


文档字符串处理工具的工作方式不是删除左边的所有空格,而是删除第一个缩进行。这种策略更复杂一些,允许您缩进并在后处理的字符串中尊重它。