Jak usunąć dodatkowe wcięcia w Pythonie Triple quoted multi-line strings?

Mam edytor Pythona, w którym użytkownik wprowadza skrypt lub kod, który jest następnie umieszczany w głównej metodzie za kulisami, jednocześnie mając każdą linię wciętą. Problem polega na tym, że jeśli użytkownik ma ciąg wielowierszowy, wcięcia wykonane w całym skrypcie wpływają na ciąg, wstawiając tabulator w każdej spacji. Skrypt problemowy byłby czymś tak prostym jak:

"""foo
bar
foo2"""

Więc gdy w głównej metodzie wyglądałoby to tak:

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

I ciąg będzie teraz mieć dodatkową kartę na początku każdej linii.

Author: codeforester, 2009-09-11

7 answers

Więc jeśli mam go poprawnie, bierzesz cokolwiek użytkownik wprowadza, wciąć go poprawnie i dodać go do reszty programu (a następnie uruchomić cały program).

Więc po wprowadzeniu danych użytkownika do programu, możesz uruchomić regex, który w zasadzie usuwa wymuszone wcięcia z powrotem. Coś w stylu: w ciągu trzech cudzysłowów, Zamień wszystkie "nowe znaczniki linii", a następnie cztery spacje (lub tabulator) na"nowy znacznik linii".

 -12
Author: FlorianH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-14 13:39:02

Texttwrap.dedent z biblioteki standardowej służy do automatycznego cofania dziwacznego wcięcia.

 134
Author: thraxil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 19:02:18

Z tego, co widzę, lepszą odpowiedzią może być inspect.cleandoc, która robi wiele z tego, co robi textwrap.dedent, ale także rozwiązuje problemy, które ma textwrap.dedent z linią wiodącą.

Poniższy przykład pokazuje różnice:

>>> import textwrap
>>> import inspect
>>> x = """foo bar
    baz
    foobar
    foobaz
    """
>>> inspect.cleandoc(x)
'foo bar\nbaz\nfoobar\nfoobaz'
>>> textwrap.dedent(x)
'foo bar\n    baz\n    foobar\n    foobaz\n'
>>> y = """
...     foo
...     bar
... """
>>> textwrap.dedent(y)
'\nfoo\nbar\n'
>>> inspect.cleandoc(y)
'foo\nbar'
>>> z = """\tfoo
bar\tbaz
"""
>>> textwrap.dedent(z)
'\tfoo\nbar\tbaz\n'
>>> inspect.cleandoc(z)
'foo\nbar     baz'

Zauważ, że inspect.cleandoc rozszerza również wewnętrzne tabulatory na spacje. To może być nieodpowiednie dla jednego przypadku użycia, ale działa dobrze dla mnie.

 49
Author: bbenne10,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-11 13:56:18

To, co następuje po pierwszej linii wielowierszowego łańcucha, jest jego częścią i nie jest traktowane jako wcięcie przez parser. Możesz swobodnie pisać:

def main():
    """foo
bar
foo2"""
    pass
I zrobi to, co trzeba.

Z drugiej strony, to nie jest czytelne, a Python o tym wie. Jeśli więc docstring zawiera białe znaki w drugiej linii, ta ilość białych znaków jest usuwana, gdy używasz help() do wyświetlania docstringu. Tak więc help(main) i poniżej help(main2) dają tę samą pomoc info.

def main2():
    """foo
    bar
    foo2"""
    pass
 19
Author: SingleNegationElimination,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-06 13:35:11

Jedyny sposób jaki widzę-to rozebranie pierwszych n tabulatorów dla każdej linii zaczynającej się od drugiej, gdzie n jest znaną identyfikacją głównej metody.

Jeśli identyfikator nie jest wcześniej znany - możesz dodać końcową nową linię przed wstawieniem jej i usunąć liczbę tabulatorów z ostatniej linii...

Trzecim rozwiązaniem jest analiza danych i znalezienie początku cytatu wielowierszowego i nie dodawanie identyfikatora do każdej linii, dopóki nie zostanie ona zamknięta.

Think there is a better rozwiązanie..

 1
Author: Mikhail Churbanov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-11 18:16:09

Pokazując różnicę między textwrap.dedent i inspect.cleandoc z nieco większą klarownością:

Zachowanie z czołówką bez wcięcia

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

Wyjście

string1 plain='String\nwith\nno indentation\n       '
string1 inspect.cleandoc='String\nwith\nno indentation\n       '
string1 texwrap.dedent='String\nwith\nno indentation\n'
string2 plain='String\n        with\n        indentation\n       '
string2 inspect.cleandoc='String\nwith\nindentation'
string2 texwrap.dedent='String\n        with\n        indentation\n'

Zachowanie z wciętą częścią wiodącą

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

Wyjście

string1 plain='\nString\nwith\nno indentation\n       '
string1 inspect.cleandoc='String\nwith\nno indentation\n       '
string1 texwrap.dedent='\nString\nwith\nno indentation\n'
string2 plain='\n        String\n        with\n        indentation\n       '
string2 inspect.cleandoc='String\nwith\nindentation'
string2 texwrap.dedent='\nString\nwith\nindentation\n'
 0
Author: codeforester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-13 23:56:49

Chciałem zachować dokładnie to, co znajduje się pomiędzy wersami potrójnego cytowania, usuwając tylko wspólne wcięcie wiodące. Stwierdziłem, że texwrap.dedent i inspect.cleandoc nie zrobiły tego całkiem dobrze, więc napisałem ten. Używa 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

Może to zacytować dowolny ciąg znaków z dowolnym wcięciem. Chciałem, aby domyślnie zawierał kończący się nowy wiersz, ale z opcją usunięcia go, aby mógł ładnie cytować dowolny ciąg znaków.

Przykład:

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

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

Drugi ciąg ma 4 spacje wspólnego wcięcia, ponieważ końcowy """ jest wcięte mniej niż cytowany tekst:

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

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

Myślałem, że to będzie prostsze, inaczej nie zawracałbym sobie tym głowy!

 0
Author: Sam Watkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-24 09:01:04