Indentación adecuada para cadenas multilínea Python

¿Cuál es la sangría adecuada para las cadenas multilínea Python dentro de una función?

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

o

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

¿o algo mas?

Parece un poco extraño tener la cadena colgando fuera de la función en el primer ejemplo.

Probablemente quieras alinearte con el """

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

Dado que las nuevas líneas y espacios están incluidos en la cadena, deberá procesarla posteriormente. Si no desea hacer eso y tiene una gran cantidad de texto, es posible que desee almacenarlo por separado en un archivo de texto. Si un archivo de texto no funciona bien para su aplicación y usted no desea realizar un postproceso, probablemente lo haga

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

Si desea postprocesar una cadena multilínea para recortar las partes que no necesita, debe considerar el módulo de textwrap o la técnica para postprocesar las cadenas de documentos presentadas en 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) 

La función textwrap.dedent permite comenzar con la sangría correcta en la fuente y luego textwrap.dedent del texto antes de usarla.

La compensación, como lo señalan algunos otros, es que esta es una llamada de función adicional en el literal; Tenga esto en cuenta al decidir dónde colocar estos literales en su código.

 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) 

El final del \ en el literal del mensaje de registro es para asegurar que el salto de línea no esté en el literal; de esa manera, el literal no comienza con una línea en blanco, y en su lugar comienza con la siguiente línea completa.

El valor de retorno de textwrap.dedent es la cadena de entrada con todas las sangrías de espacios en blanco textwrap.dedent comunes eliminadas en cada línea de la cadena. Así que el valor log_message anterior será:

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

Una opción que parece faltar en las otras respuestas (solo mencionada en el comentario de naxa) es la siguiente:

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

Esto permitirá una alineación correcta, unirá las líneas implícitamente y seguirá manteniendo el cambio de línea, lo que, para mí, es una de las razones por las que me gustaría usar cadenas multilínea de todos modos.

No requiere ningún procesamiento posterior, pero debe agregar manualmente \n en cualquier lugar en el que desee que finalice la línea. Ya sea en línea o como una cadena separada después. Este último es más fácil de copiar y pegar.

Utilice inspect.cleandoc manera:

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

La sangría relativa se mantendrá como se espera.

Nota: es una buena práctica sangrar bloques de código lógicos en su contexto relacionado para aclarar la estructura. Por ejemplo, la cadena multilínea que pertenece a la string variable.

Algunas opciones más. En Ipython con pylab habilitado, el dedent ya está en el espacio de nombres. Lo comprobé y es de matplotlib. O se puede importar con:

 from matplotlib.cbook import dedent 

En la documentación indica que es más rápido que el equivalente de textwrap y en mis pruebas en ipython es 3 veces más rápido en promedio con mis pruebas rápidas. También tiene el beneficio de que descarta las líneas en blanco iniciales, lo que le permite ser flexible en la forma en que construye la cadena:

 """ line 1 of string line 2 of string """ """\ line 1 of string line 2 of string """ """line 1 of string line 2 of string """ 

El uso de la deducción de matplotlib en estos tres ejemplos dará el mismo resultado sensible. La función de retención de texto tendrá una línea en blanco inicial con el primer ejemplo.

La desventaja obvia es que textwrap está en la biblioteca estándar mientras que matplotlib es un módulo externo.

Algunas compensaciones aquí … las funciones de deducción hacen que su código sea más legible donde se definen las cadenas, pero requieren un procesamiento posterior para obtener la cadena en formato utilizable. En docstrings es obvio que debe usar una sangría correcta ya que la mayoría de los usos de docstring harán el procesamiento requerido.

Cuando necesito una cadena que no sea larga en mi código, encuentro el siguiente código, ciertamente feo, en el que dejo que la cadena larga salga de la sangría que la contiene. Definitivamente falla en “Lo bello es mejor que lo feo”, pero se podría argumentar que es más simple y más explícito que la alternativa de 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() 

Si desea una solución rápida y fácil y se salva de escribir nuevas líneas, puede optar por una lista, por ejemplo:

 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 

yo prefiero

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

o

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

Mis dos centavos, escapen del final de la línea para obtener las sangrías:

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

Vine aquí en busca de un simple 1-liner para eliminar / corregir el nivel de identificación de la cadena de documentos para imprimir, sin que se vea desordenado , por ejemplo, haciendo que se “cuelgue fuera de la función” dentro del script.

Esto es lo que terminé haciendo:

 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:] 

Obviamente, si está sangrando con espacios (por ejemplo, 4) en lugar de la tecla de tabulación, use algo como esto en su lugar:

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

Y no es necesario que elimines el primer carácter si quieres que tus cadenas de documentos se vean así:

  """line 1 of docstring line 2 of docstring line 3 of docstring""" print string.replace(myfunction.__doc__,'\n\t','\n') 

La primera opción es la buena, con sangría incluida. Está en estilo python: proporciona legibilidad para el código.

Para mostrarlo correctamente:

 print string.lstrip() 

Depende de cómo desee que se muestre el texto. Si desea que todo se alinee a la izquierda, entonces formatee como en el primer fragmento o itere a través de las líneas que recortan a la izquierda todo el espacio.

Para las cadenas, justo después de procesar la cadena. Para las cadenas de documentación que necesita después de procesar la función en su lugar. Aquí hay una solución para ambos que todavía es legible.

 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__ 

A veces escribo texto multilínea como una lista de cadenas y luego las concatené …

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

No es la forma más eficiente, pero es lo suficientemente eficaz para la mayoría de los casos de uso y no se confunde con su sangría o requiere que la primera línea tenga una sangría diferente a la segunda como el método de la triple cita.