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

#python

#python #кавычки #многострочный #Стриптиз

Вопрос:

У меня есть редактор python, в котором пользователь вводит скрипт или код, который затем помещается в основной метод за кулисами, при этом каждая строка имеет отступ. Проблема в том, что если у пользователя есть многострочная строка, отступ, сделанный для всего скрипта, влияет на строку, вставляя табуляцию в каждый пробел. Проблемный сценарий был бы таким простым, как:

 """foo
bar
foo2"""
 

Итак, когда в основном методе это будет выглядеть так:

 def main():
    """foo
    bar
    foo2"""
 

и теперь строка будет иметь дополнительную вкладку в начале каждой строки.

Комментарии:

1. codereview.stackexchange.com/questions/60366/…

Ответ №1:

textwrap.dedent из стандартной библиотеки предназначен для автоматической отмены дурацкого отступа.

Комментарии:

1. Стандартная библиотека никогда не перестает преподносить сюрпризы.

2. Обратите внимание, что если первая строка начинается как """foo , то в первой строке отсутствует начальный отступ, который есть в других строках, поэтому dedent ничего не будет делать. Это сработает, если вы дождетесь запуска foo на следующей строке и экранируете первую новую строку следующим образом: `»»»`

3. Чтобы устранить недостатки, о которых упоминает @ScottH, пожалуйста, ознакомьтесь с моим ответом относительно inspect.cleandoc

Ответ №2:

Из того, что я вижу, лучшим ответом здесь может быть inspect.cleandoc , который делает многое из того, что textwrap.dedent делает, но также устраняет проблемы, textwrap.dedent связанные с начальной строкой.

Приведенный ниже пример показывает различия:

 >>> import textwrap
>>> import inspect
>>> x = """foo bar
    baz
    foobar
    foobaz
    """
>>> inspect.cleandoc(x)
'foo barnbaznfoobarnfoobaz'
>>> textwrap.dedent(x)
'foo barn    bazn    foobarn    foobazn'
>>> y = """
...     foo
...     bar
... """
>>> inspect.cleandoc(y)
'foonbar'
>>> textwrap.dedent(y)
'nfoonbarn'
>>> z = """tfoo
bartbaz
"""
>>> inspect.cleandoc(z)
'foonbar     baz'
>>> textwrap.dedent(z)
'tfoonbartbazn'

 

Обратите внимание, что это inspect.cleandoc также расширяет внутренние табуляции до пробелов.
Это может быть неуместно для конкретного варианта использования, но для меня работает нормально.

Комментарии:

1. Помните, что в противном случае эти два не совсем эквивалентны, и cleandoc выполняет больше обработки, чем просто удаление отступов. По крайней мере, расширение 't' до ' '

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

3. Также можно textwrap.dedent(s).strip() избежать смены табуляции и по-прежнему обрабатывать начальные и конечные новые строки.

4. Контекст, в котором я написал этот ответ, является гораздо более общим, чем тот, в котором был задан вопрос. Я искал возможность повторно использовать строки документации для целей документации (поэтому свертывание полезно). Вы правы, что вы могли бы обработать textwrap.dedent вывод для более конкретных сценариев. Я пренебрег нюансом исходного вопроса, когда отвечал на это. Однако я считаю, что мой ответ более полезен в общем плане.

5. IDK, если это фиктивная ошибка для мира python, но следует быть осторожным, используя n somwhere в строке с тройными кавычками. inspect.cleandoc это не будет очищено. (опытный.).

Ответ №3:

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

 def main():
    """foo
bar
foo2"""
    pass
 

и это будет правильно.

С другой стороны, это не читается, и Python это знает. Итак, если строка документа содержит пробелы во второй строке, это количество пробелов удаляется при help() просмотре строки документа. Таким образом, help(main) и приведенное ниже help(main2) дает ту же справочную информацию.

 def main2():
    """foo
    bar
    foo2"""
    pass
 

Комментарии:

1. Спасибо за ответ. К сожалению, отступ полностью автоматизирован, поскольку мой код считывается в скрипте как строка (на Java) и делает отступы в каждой строке в этой строке.

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

3. @tribbloid специальная логика для строк документации специфична для варианта использования help() , когда по умолчанию делается что-то приятное. Чтобы использовать ту же логику выделения в других местах, вы можете использовать textwrap.dedent() , как описано в основном в любом другом ответе на этот вопрос.

Ответ №4:

Показ разницы между textwrap.dedent и inspect.cleandoc с немного большей ясностью:

Поведение с начальной частью без отступа

 import textwrap
import inspect

string1="""String
with
no indentation
       """
string2="""String
        with
        indentation
       """
print('string1 plain='   repr(string1))
print('string1 inspect.cleandoc='   repr(inspect.cleandoc(string1)))
print('string1 texwrap.dedent='   repr(textwrap.dedent(string1)))
print('string2 plain='   repr(string2))
print('string2 inspect.cleandoc='   repr(inspect.cleandoc(string2)))
print('string2 texwrap.dedent='   repr(textwrap.dedent(string2)))
 

Вывод

 string1 plain='Stringnwithnno indentationn       '
string1 inspect.cleandoc='Stringnwithnno indentationn       '
string1 texwrap.dedent='Stringnwithnno indentationn'
string2 plain='Stringn        withn        indentationn       '
string2 inspect.cleandoc='Stringnwithnindentation'
string2 texwrap.dedent='Stringn        withn        indentationn'
 

Поведение с отступом в начальной части

 string1="""
String
with
no indentation
       """
string2="""
        String
        with
        indentation
       """

print('string1 plain='   repr(string1))
print('string1 inspect.cleandoc='   repr(inspect.cleandoc(string1)))
print('string1 texwrap.dedent='   repr(textwrap.dedent(string1)))
print('string2 plain='   repr(string2))
print('string2 inspect.cleandoc='   repr(inspect.cleandoc(string2)))
print('string2 texwrap.dedent='   repr(textwrap.dedent(string2)))
 

Вывод

 string1 plain='nStringnwithnno indentationn       '
string1 inspect.cleandoc='Stringnwithnno indentationn       '
string1 texwrap.dedent='nStringnwithnno indentationn'
string2 plain='n        Stringn        withn        indentationn       '
string2 inspect.cleandoc='Stringnwithnindentation'
string2 texwrap.dedent='nStringnwithnindentationn'
 

Ответ №5:

Я хотел сохранить именно то, что находится между строками в тройных кавычках, удалив только общий начальный отступ. Я нашел это texwrap.dedent и inspect.cleandoc сделал это не совсем правильно, поэтому я написал это. Он использует os.path.commonprefix .

 import re
from os.path import commonprefix

def ql(s, eol=True):
    lines = s.splitlines()
    l0 = None
    if lines:
        l0 = lines.pop(0) or None
    common = commonprefix(lines)
    indent = re.match(r's*', common)[0]
    n = len(indent)
    lines2 = [l[n:] for l in lines]
    if not eol and lines2 and not lines2[-1]:
        lines2.pop()
    if l0 is not None:
        lines2.insert(0, l0)
    s2 = "n".join(lines2)
    return s2
 

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

Пример:

 print(ql("""
     Hello
    |---/|
    | o_o |
     _^_/
    """))

print(ql("""
         World
        |---/|
        | o_o |
         _^_/
    """))
 

Вторая строка имеет 4 пробела общего отступа, потому что конечный """ отступ меньше, чем текст в кавычках:

  Hello
|---/|
| o_o |
 _^_/

     World
    |---/|
    | o_o |
     _^_/
 

Я думал, что это будет проще, иначе я бы не стал этим заниматься!

Ответ №6:

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

Если это обозначение не известно заранее — вы можете добавить завершающую новую строку перед ее вставкой и удалить количество табуляций из последней строки…

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

Думаю, есть лучшее решение..

Комментарии:

1. Спасибо за ответ. Итак, вы предлагаете мне удалить каждую строку из вставленного отступа? Я в замешательстве…

Ответ №7:

У меня была похожая проблема: я хотел, чтобы моя строка в тройных кавычках имела отступ, но я не хотел, чтобы в строке были все эти пробелы в начале каждой строки. Раньше я re исправлял свою проблему:

         print(re.sub('n *','n', f"""Content-Type: multipart/mixed; boundary="===============9004758485092194316=="
`           MIME-Version: 1.0
            Subject: Get the reader's attention here!
            To: recipient@email.com

            --===============9004758485092194316==
            Content-Type: text/html; charset="us-ascii"
            MIME-Version: 1.0
            Content-Transfer-Encoding: 7bit

            Very important message goes here - you can even use <b>HTML</b>.
            --===============9004758485092194316==--
        """))
 

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

Компромисс, который я сделал, заключался в том, что я оставил Content-Type в первой строке, потому regex что используемый мной не удалял начальный n (который сломал электронную почту). Если бы это меня достаточно беспокоило, я думаю, я мог бы добавить lstrip следующим образом:

 print(re.sub('n *','n', f"""
    Content-Type: ...
""").lstrip()
 

Прочитав эту страницу 10-летней давности, я решил придерживаться, re.sub поскольку я действительно не понимал всех нюансов textwrap и inspect .

Ответ №8:

Есть гораздо более простой способ:

     foo = """first line
             nsecond line"""
 

Комментарии:

1. Для этого потребуется вручную добавить новую строку и добавить пробелы для отступов к предыдущей строке.

2. Не уверен, в чем проблема, чтобы добавить » n». Если вы форматируете с нуля, его легко добавить, не видя никаких проблем с добавлением дополнительных символов к пользовательскому вводу или извлеченному тексту. И это ничего не добавляет к строке, заканчивающейся на «». Возможно, это подходит не для всех вариантов использования, но для меня это сработало намного лучше, чем все, что я смог найти.

3. Он добавляет пробелы для отступов (после), и это не решает исходную проблему, поскольку данные поступили от пользователя.

Ответ №9:

Итак, если я правильно понял, вы берете все, что вводит пользователь, делаете правильный отступ и добавляете его к остальной части вашей программы (а затем запускаете всю эту программу).

Итак, после того, как вы введете пользовательский ввод в свою программу, вы можете запустить регулярное выражение, которое в основном возвращает этот принудительный отступ. Что-то вроде: в трех кавычках замените все «маркеры новой строки», за которыми следуют четыре пробела (или табуляция), только «маркером новой строки».

Комментарии:

1. да, точно. Это единственное возможное решение, которое я придумал. Не уверен, почему я не продолжил это … я думаю, мне, возможно, придется это сделать, если ничего лучшего не придумается.

2. предложение @thraxil использовать textwrap.dedent — это правильный путь. Подумайте об изменении принятого ответа.

3. Ответ @ChrisCalo @ bbenne10 еще лучше