#python
#python #кавычки #многострочный #Стриптиз
Вопрос:
У меня есть редактор python, в котором пользователь вводит скрипт или код, который затем помещается в основной метод за кулисами, при этом каждая строка имеет отступ. Проблема в том, что если у пользователя есть многострочная строка, отступ, сделанный для всего скрипта, влияет на строку, вставляя табуляцию в каждый пробел. Проблемный сценарий был бы таким простым, как:
"""foo
bar
foo2"""
Итак, когда в основном методе это будет выглядеть так:
def main():
"""foo
bar
foo2"""
и теперь строка будет иметь дополнительную вкладку в начале каждой строки.
Комментарии:
Ответ №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 еще лучше