一行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语法中的INDENT
和DEDENT
标记而言,它是零级的,但是你可能不会有同样的感觉。
如果你想这样做,你将不得不标记整个文件直到调用点并计算INDENT
和DEDENT
标记。 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