while igual else elif ejemplos condiciones python reflection metaprogramming indentation tokenize

igual - reflection python



¿Puede una línea de código Python conocer su nivel de anidación de sangría? (5)

Para resolver el problema "real" que lleva a su pregunta, puede implementar un administrador de contexto que realice un seguimiento del nivel de sangría y haga que la estructura de bloques with en el código corresponda a los niveles de sangría de la salida. De esta manera, la sangría del código aún refleja la sangría de salida sin acoplar demasiado los dos. Todavía es posible refactorizar el código en diferentes funciones y tener otras sangrías basadas en la estructura del código que no interfiera con la sangría de salida.

#!/usr/bin/env python # coding: utf8 from __future__ import absolute_import, division, print_function class IndentedPrinter(object): def __init__(self, level=0, indent_with='' ''): self.level = level self.indent_with = indent_with def __enter__(self): self.level += 1 return self def __exit__(self, *_args): self.level -= 1 def print(self, arg='''', *args, **kwargs): print(self.indent_with * self.level + str(arg), *args, **kwargs) def main(): indented = IndentedPrinter() indented.print(indented.level) with indented: indented.print(indented.level) with indented: indented.print(''Hallo'', indented.level) with indented: indented.print(indented.level) indented.print(''and back one level'', indented.level) if __name__ == ''__main__'': main()

Salida:

0 1 Hallo 2 3 and back one level 2

De algo como esto:

print(get_indentation_level()) print(get_indentation_level()) print(get_indentation_level())

Me gustaría obtener algo como esto:

1 2 3

¿Puede el código leerse de esta manera?

Todo lo que quiero es que la salida de las partes más anidadas del código esté más anidada. De la misma manera que esto hace que el código sea más fácil de leer, haría que la salida sea más fácil de leer.

Por supuesto, podría implementar esto manualmente, usando, por ejemplo, .format() , pero lo que tenía en mente era una función de impresión personalizada que print(i*'' '' + string) donde i es el nivel de sangría. Esta sería una forma rápida de hacer una salida legible en mi terminal.

¿Hay una mejor manera de hacer esto que evite el formato manual minucioso?


Puede usar sys.current_frame.f_lineno para obtener el número de línea. Luego, para encontrar el número de nivel de sangría, necesita encontrar la línea anterior con cero sangría, luego restando el número de línea actual del número de esa línea obtendrá el número de sangría:

import sys current_frame = sys._getframe(0) def get_ind_num(): with open(__file__) as f: lines = f.readlines() current_line_no = current_frame.f_lineno to_current = lines[:current_line_no] previous_zoro_ind = len(to_current) - next(i for i, line in enumerate(to_current[::-1]) if not line[0].isspace()) return current_line_no - previous_zoro_ind

Manifestación:

if True: print get_ind_num() if True: print(get_ind_num()) if True: print(get_ind_num()) if True: print(get_ind_num()) # Output 1 3 5 6

Si desea el número del nivel de sangría basado en las líneas anteriores con : puede hacerlo con un pequeño cambio:

def get_ind_num(): with open(__file__) as f: lines = f.readlines() current_line_no = current_frame.f_lineno to_current = lines[:current_line_no] previous_zoro_ind = len(to_current) - next(i for i, line in enumerate(to_current[::-1]) if not line[0].isspace()) return sum(1 for line in lines[previous_zoro_ind-1:current_line_no] if line.strip().endswith('':''))

Manifestación:

if True: print get_ind_num() if True: print(get_ind_num()) if True: print(get_ind_num()) if True: print(get_ind_num()) # Output 1 2 3 3

Y como respuesta alternativa aquí hay una función para obtener el número de sangría (espacio en blanco):

import sys from itertools import takewhile current_frame = sys._getframe(0) def get_ind_num(): with open(__file__) as f: lines = f.readlines() return sum(1 for _ in takewhile(str.isspace, lines[current_frame.f_lineno - 1]))


Sí, eso es definitivamente posible, aquí hay un ejemplo de trabajo:

import inspect def get_indentation_level(): callerframerecord = inspect.stack()[1] frame = callerframerecord[0] info = inspect.getframeinfo(frame) cc = info.code_context[0] return len(cc) - len(cc.lstrip()) if 1: print get_indentation_level() if 1: print get_indentation_level() if 1: print get_indentation_level()


Si desea una sangría en términos de nivel de anidación en lugar de espacios y pestañas, las cosas se ponen difíciles. Por ejemplo, en el siguiente código:

if True: print( get_nesting_level())

la llamada a get_nesting_level realidad está anidada a un nivel de profundidad, a pesar del hecho de que no hay espacios en blanco get_nesting_level en la línea de la llamada get_nesting_level . Mientras tanto, en el siguiente código:

print(1, 2, get_nesting_level())

la llamada a get_nesting_level está anidada a cero niveles de profundidad, a pesar de la presencia de espacios en blanco iniciales en su línea.

En el siguiente código:

if True: if True: print(get_nesting_level()) if True: print(get_nesting_level())

las dos llamadas a get_nesting_level están en diferentes niveles de anidación, a pesar de que el espacio en blanco inicial es idéntico.

En el siguiente código:

if True: print(get_nesting_level())

¿Es eso cero niveles anidados, o uno? En términos de tokens DEDENT y DEDENT en la gramática formal, tiene cero niveles de profundidad, pero es posible que no sienta lo mismo.

Si desea hacer esto, tendrá que tokenizar todo el archivo hasta el punto de la llamada y contar los tokens DEDENT y DEDENT . El módulo tokenize sería muy útil para tal función:

import inspect import tokenize def get_nesting_level(): caller_frame = inspect.currentframe().f_back filename, caller_lineno, _, _, _ = inspect.getframeinfo(caller_frame) with open(filename) as f: indentation_level = 0 for token_record in tokenize.generate_tokens(f.readline): token_type, _, (token_lineno, _), _, _ = token_record if token_lineno > caller_lineno: break elif token_type == tokenize.INDENT: indentation_level += 1 elif token_type == tokenize.DEDENT: indentation_level -= 1 return indentation_level


>>> import inspect >>> help(inspect.indentsize) Help on function indentsize in module inspect: indentsize(line) Return the indent size, in spaces, at the start of a line of text.