Правильный отступ для многострочных строк Python

Каков правильный отступ для многострочных строк Python внутри функции?

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

или

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

или что-то еще?

Похоже, что в первом примере строка, зависающая вне функции, выглядит странно.

Ответ 1

Вы, вероятно, хотите выстроить строку с """

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

Так как новые строки и пробелы включены в строку, вам придется ее обработать. Если вы не хотите этого делать, и у вас есть много текста, вы можете захотеть сохранить его отдельно в текстовом файле. Если текстовый файл не подходит для вашего приложения, и вы не хотите выполнять постпроцесс, я бы, вероятно, пошел с

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

Если вы хотите обработать многострочную строку для обрезки частей, которые вам не нужны, вы должны рассмотреть модуль textwrap или техника для постобработки docstrings, представленная в 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)

Ответ 2

Функция 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!

Ответ 3

Используйте inspect.cleandoc примерно так:

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

Относительный отступ будет сохранен, как и ожидалось. Как прокомментировал ниже, если вы хотите сохранить предшествующие пустые строки, используйте textwrap.dedent. Однако это также сохраняет первый разрыв строки.

Примечание. Рекомендуется делать отступы логических блоков кода в соответствующем контексте для уточнения структуры. Например. многострочная строка, принадлежащая переменной string.

Ответ 4

Один из вариантов, который, как представляется, отсутствует в других ответах (упоминается только в комментариях naxa):

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

Это позволит правильно выравнивать, присоединяться к строкам неявно и по-прежнему сохранять сдвиг строки, который для меня является одной из причин, по которым я хотел бы использовать многострочные строки в любом случае.

Это не требует постобработки, но вам нужно вручную добавить \n в любом месте, которое вы хотите, чтобы линия закончилась. Либо встроенный, либо как отдельная строка после. Последнему легче скопировать-вставить.

Ответ 5

Еще несколько вариантов. В Ipython с включенным pylab разделитель уже находится в пространстве имен. Я проверил, и это от 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 на этих трех примерах даст тот же разумный результат. Функция разделителя textwrap будет иметь ведущую пустую строку с 1-м примером.

Очевидным недостатком является то, что textwrap находится в стандартной библиотеке, а matplotlib - внешний модуль.

Некоторые компромиссы здесь... функции разделителя делают ваш код более читаемым, когда строки определяются, но требуют обработки позже, чтобы получить строку в удобном для использования формате. В docstrings очевидно, что вы должны использовать правильный отступ, поскольку большинство применений docstring выполнит требуемую обработку.

Когда мне нужен недолговая строка в моем коде, я нахожу следующий, по общему признанию, уродливый код, где я позволяю длинной строке выпадать из вложенного отступа. Определенно терпит неудачу в том, что "Красиво лучше, чем уродливое". Но можно утверждать, что он проще и более явным, чем условная альтернатива.

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

Ответ 6

Если вы хотите быстрое и простое решение и избавитесь от ввода новых строк, вы можете выбрать список, например:

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

Ответ 7

Я предпочитаю

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

или

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

Ответ 8

Мои два цента, избегайте конца строки, чтобы получить отступы:

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

Ответ 9

Я пришел сюда, чтобы найти простой 1-лайнер для удалить/исправить уровень идентификации для docstring для печати, , не делая его выглядящим неопрятным, например, путем создания он "висит вне функции" в script.

Вот что я в итоге сделал:

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), а вместо клавиши табуляции используйте что-то вроде этого:

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

И вам не нужно удалять первый символ, если вам нравится, чтобы ваши docstrings выглядели следующим образом:

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

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

Ответ 10

Первый вариант - хороший, с отступом. Это в стиле Python - обеспечивает читабельность кода.

Чтобы отобразить это правильно:

print string.lstrip()

Ответ 11

Это зависит от того, как вы хотите, чтобы текст отображался. Если вы хотите, чтобы все было выровнено по левому краю, либо отформатируйте его, как в первом фрагменте, либо выполните итерацию по линиям, оставив все пробел.

Ответ 12

Для строк вы можете сразу после обработки строки. Для docstrings вам нужно выполнить процедуру вместо этого. Вот решение для обоих, которое еще доступно для чтения.

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__

Ответ 13

Иногда я пишу многострочный текст в виде списка строк, затем объединяю их...

    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",
    ])

Это не самый эффективный способ, но он достаточно производительный для большинства случаев использования, и он не портит ваш отступ или не требует, чтобы первая строка отличалась от второй, как метод с тройными кавычками.