一行Python代码能够知道它的缩进嵌套级别吗?

从这样的事情:

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

我想得到这样的东西:

 1 2 3 

代码能以这种方式读取它自己吗?

我想要的是从更多的嵌套部分的代码更多的嵌套输出。 以同样的方式,这使得代码更容易阅读,这将使输出更容易阅读。

当然,我可以手动实现,例如.format() ,但是我想到的是一个自定义的打印函数,它将print(i*' ' + string) ,其中i是缩进级别。 这将是在我的terminal上进行可读输出的快速方法。

有没有更好的方法来避免艰苦的手动格式?

如果你想在嵌套级别,而不是空格和制表符缩进,事情变得棘手。 例如,在以下代码中:

 if True: print( get_nesting_level()) 

get_nesting_level的调用实际上嵌套了一层,尽pipeget_nesting_level调用的行上没有前导空白。 同时,在下面的代码中:

 print(1, 2, get_nesting_level()) 

get_nesting_level的调用嵌套深度为零,尽pipe在其行上存在前导空格。

在下面的代码中:

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

get_nesting_level的两个调用位于不同的嵌套级别,尽pipe前导空格是相同的。

在下面的代码中:

 if True: print(get_nesting_level()) 

是嵌套的零水平,还是一个? 就forms语法中的INDENTDEDENT标记而言,它是零级的,但是你可能不会有同样的感觉。


如果你想这样做,你将不得不标记整个文件直到调用点并计算INDENTDEDENT标记。 tokenize模块对于这样一个function非常有用:

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

您可以使用sys.current_frame.f_lineno来获取行号。 然后为了find缩进级别的数量,你需要find零缩进的上一行,然后从该行的数字减去当前的行号,你会得到缩进的数量:

 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 

演示:

 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 

如果你想根据前面的行的缩进级别的数字:你可以做一点改变:

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

演示:

 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 

作为一个替代的答案,这里是一个获取缩进数(空格)的函数:

 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. 

为了解决导致你的问题的“真实”问题,你可以实现一个跟踪缩进级别的上下文pipe理器,并使代码中的块结构对应于输出的缩进级别。 这样的代码缩进仍然反映输出缩进,而不是耦合太多。 仍然有可能将代码重构成不同的函数,并且还有其他基于代码结构的缩进不会与输出缩进相混淆。

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

输出:

 0 1 Hallo 2 3 and back one level 2