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.