개발/Python

한 줄의 Python 코드가 들여 쓰기 중첩 수준을 알 수 있습니까?

MinorMan 2021. 1. 21. 11:20
반응형

<질문>

다음과 같이 :

print(get_indentation_level())

    print(get_indentation_level())

        print(get_indentation_level())

다음과 같은 것을 얻고 싶습니다.

1
2
3

이런 식으로 코드 자체를 읽을 수 있습니까?

내가 원하는 것은 코드의 더 많은 중첩 부분의 출력이 더 중첩되는 것입니다. 이렇게하면 코드를 더 쉽게 읽을 수있는 것과 마찬가지로 출력을 더 쉽게 읽을 수 있습니다.

물론 예를 사용하여 수동으로 구현할 수 있습니다..format(),하지만 제가 염두에 둔 것은 사용자 정의 인쇄 기능이었습니다.print(i*' ' + string)어디i들여 쓰기 수준입니다. 이것은 터미널에서 읽을 수있는 출력을 만드는 빠른 방법입니다.

힘든 수동 서식 지정을 피하는 더 좋은 방법이 있습니까?


<답변1>

공백과 탭이 아닌 중첩 수준 측면에서 들여 쓰기를 원하는 경우 작업이 까다로워집니다. 예를 들어, 다음 코드에서 :

if True:
    print(
get_nesting_level())

호출get_nesting_level줄에 선행 공백이 없다는 사실에도 불구하고 실제로 한 수준 깊이 중첩됩니다.get_nesting_level요구. 한편, 다음 코드에서 :

print(1,
      2,
      get_nesting_level())

호출get_nesting_level행에 선행 공백이 있음에도 불구하고 0 레벨 깊이로 중첩됩니다.

다음 코드에서 :

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

if True:
    print(get_nesting_level())

두 전화get_nesting_level선행 공백이 동일 함에도 불구하고 서로 다른 중첩 수준에 있습니다.

다음 코드에서 :

if True: print(get_nesting_level())

중첩 된 0 레벨입니까, 아니면 1입니까? 측면에서INDENTDEDENT형식적인 문법에서 토큰은 0 레벨 깊이이지만 같은 느낌이 들지 않을 수도 있습니다.

이렇게하려면 호출 시점까지 전체 파일을 토큰 화하고 계산해야합니다.INDENTDEDENT토큰. 그만큼tokenize모듈은 이러한 기능에 매우 유용합니다.

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

<답변2>

네, 확실히 가능합니다. 여기에 실제적인 예가 있습니다.

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

<답변3>

당신이 사용할 수있는sys.current_frame.f_lineno줄 번호를 얻으려면. 그런 다음 들여 쓰기 수준의 수를 찾으려면 들여 쓰기가 0 인 이전 줄을 찾은 다음 해당 줄의 번호에서 현재 줄 번호를 빼야합니다. 들여 쓰기 수를 얻습니다.

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

<답변4>

질문으로 이어지는 "실제"문제를 해결하기 위해 들여 쓰기 수준을 추적하고 확인하는 컨텍스트 관리자를 구현할 수 있습니다.with코드의 블록 구조는 출력의 들여 쓰기 수준에 해당합니다. 이런 식으로 코드 들여 쓰기는 둘 다 너무 많이 결합하지 않고도 출력 들여 쓰기를 반영합니다. 코드를 다른 함수로 리팩터링하고 출력 들여 쓰기를 엉망으로 만들지 않는 코드 구조를 기반으로 다른 들여 쓰기를하는 것은 여전히 가능합니다.

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

<답변5>

>>> 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.
반응형