147 votes

Une ligne de code Python sache son niveau d’imbrication indentation ?

À partir de quelque chose comme ceci:

print(get_indentation_level())

    print(get_indentation_level())

        print(get_indentation_level())

Je voudrais obtenir quelque chose comme ceci:

1
2
3

Le code peut-il se lire de cette façon?

Tout ce que je veux est la sortie de la plus imbriquée parties du code pour être plus imbriqués. De la même manière que cela rend le code plus facile à lire, ce serait rendre la sortie plus facile à lire.

Bien sûr, je pourrais mettre en œuvre manuellement, à l'aide par exemple, .format(), mais de ce que j'avais dans l'idée d'une fonction d'impression personnalisée qui serait - print(i*' ' + string)i est le niveau d'indentation. Ce serait une façon rapide de le rendre lisible sortie sur mon terminal.

Est-il une meilleure façon de faire ce qui évite laborieux manuel de mise en forme?

114voto

user2357112 Points 37737

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

if True:
    print(
get_nesting_level())

l'appel à l' get_nesting_level est en fait à-dire un niveau profond, malgré le fait qu'il n'y a aucun espace sur la ligne de l' get_nesting_level appel. Pendant ce temps, dans le code suivant:

print(1,
      2,
      get_nesting_level())

l'appel à l' get_nesting_level est imbriqué à zéro des niveaux de profondeur, malgré la présence des principaux espaces sur sa ligne.

Dans le code suivant:

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

if True:
    print(get_nesting_level())

les deux appels d' get_nesting_level sont à différents niveaux d'imbrication, malgré le fait que le premier espace est identique.

Dans le code suivant:

if True: print(get_nesting_level())

c'est que imbriquée zéro, ou un seul? En termes de INDENT et DEDENT de jetons dans la grammaire formelle, c'est le zéro de niveaux de profondeur, mais vous pourriez ne pas sentir de la même manière.


Si vous voulez faire cela, vous allez devoir marquer l'ensemble du dossier jusqu'au moment de l'appel et count INDENT et DEDENT des jetons. L' tokenize module serait très utile pour cette 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

21voto

BPL Points 5420

Oui, c’est sans aucun doute possible, voici un exemple de travail :

9voto

Kasramvd Points 32864

Vous pouvez utiliser sys.current_frame.f_lineno afin d'obtenir le numéro de la ligne. Puis dans l'ordre pour trouver le nombre de niveau d'indentation vous avez besoin de trouver la ligne précédente avec zéro indentation alors soustrayant le numéro de ligne courant à partir de cette ligne du numéro que vous obtiendrez 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 de retrait basée sur la previouse lignes avec : vous pouvez le faire avec un peu de 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.strip().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 une alternative de réponse ici est une fonction permettant d'obtenir le nombre d'indentation (des espaces):

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

7voto

BlackJack Points 2889

Pour résoudre le "vrai" problème qui mènent à votre question, vous pouvez implémenter un contextmanager qui assure le suivi de la mise en retrait niveau et faire de l' with la structure de bloc dans le code correspondent aux niveaux d'indentation de la sortie. De cette façon, le code de l'indentation reflète toujours la sortie de l'indentation sans couplage à la fois trop. Il est encore possible de refactoriser le code dans des fonctions différentes et ont d'autres indentations basé sur le code de la structure de ne pas jouer avec la sortie de l'indentation.

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

6voto

Craig Burgler Points 1578
>>> 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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X