Une ligne de code Python peut-elle connaître son niveau d’imbrication de l’indentation?

De quelque chose comme ça:

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

Je voudrais obtenir quelque chose comme ça:

 1 2 3 

Le code peut-il se lire de cette manière?

Tout ce que je veux, c’est la sortie des parties les plus nestedes du code pour être plus nestedes. De la même manière que cela facilite la lecture du code, cela rendrait la lecture plus facile.

Bien sûr, je pourrais l’implémenter manuellement, en utilisant par exemple .format() , mais je pensais à une fonction d’impression personnalisée qui print(i*' ' + ssortingng)i est le niveau d’indentation. Ce serait un moyen rapide de rendre la sortie lisible sur mon terminal.

Existe-t-il une meilleure façon de procéder, ce qui évite un formatage manuel fastidieux?

Si vous voulez une indentation en termes de niveau d’imbrication plutôt que d’espaces et d’tabs, les choses deviennent difficiles. Par exemple, dans le code suivant:

 if True: print( get_nesting_level()) 

l’appel à get_nesting_level est en fait nested à un niveau, même s’il n’y a pas d’espaces en tête sur la ligne de l’appel get_nesting_level . Pendant ce temps, dans le code suivant:

 print(1, 2, get_nesting_level()) 

l’appel à get_nesting_level est nested à zéro, malgré la présence d’espaces blancs sur sa ligne.

Dans le code suivant:

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

Les deux appels à get_nesting_level sont à des niveaux d’imbrication différents, même si les espaces blancs sont identiques.

Dans le code suivant:

 if True: print(get_nesting_level()) 

Est-ce que ce sont des niveaux nichés, ou un seul? En termes de jetons DEDENT et DEDENT dans la grammaire formelle, il s’agit de niveaux zéro, mais vous ne ressentirez peut-être pas la même chose.


Si vous voulez faire cela, vous devrez numéroter le fichier entier jusqu’au sharepoint l’appel et compter les jetons DEDENT et DEDENT . Le module de tokenize serait très utile pour une telle fonction:

 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 

Oui, c’est certainement possible, voici un exemple concret:

 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.lssortingp()) if 1: print get_indentation_level() if 1: print get_indentation_level() if 1: print get_indentation_level() 

Vous pouvez utiliser sys.current_frame.f_lineno pour obtenir le numéro de ligne. Ensuite, pour trouver le nombre d’indentation dont vous avez besoin pour trouver la ligne précédente avec une indentation nulle, vous devez soustraire le numéro de ligne actuel du numéro de cette ligne pour obtenir le nombre d’indentation:

 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 

Démo:

 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 vous voulez le numéro du niveau d’indentation basé sur les lignes de previouse avec : vous pouvez simplement le faire avec un petit changement:

 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.ssortingp().endswith(':')) 

Démo:

 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 

Et comme solution alternative, voici une fonction pour obtenir le nombre d’indentation (espace blanc):

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

Pour résoudre le problème “réel” qui a conduit à votre question, vous pouvez implémenter un gestionnaire de contexte qui garde la trace du niveau d’indentation et faire en sorte que la structure with block du code corresponde aux niveaux d’indentation de la sortie. De cette façon, l’indentation du code reflète toujours l’indentation de sortie sans trop se coupler. Il est toujours possible de refactoriser le code en différentes fonctions et d’avoir d’autres indentations basées sur une structure de code qui ne perturbe pas l’indentation de sortie.

 #!/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() 

Sortie:

 0 1 Hallo 2 3 and back one level 2