파이썬 코드 줄이 들여 쓰기 중첩 수준을 알 수 있습니까?


149

이런 식으로 :

print(get_indentation_level())

    print(get_indentation_level())

        print(get_indentation_level())

나는 이와 같은 것을 얻고 싶다 :

1
2
3

이 방법으로 코드를 읽을 수 있습니까?

내가 원하는 것은 코드의 더 중첩 된 부분의 출력이 더 중첩 될 것입니다. 이렇게하면 코드를보다 쉽게 ​​읽을 수 있으며 출력을보다 쉽게 ​​읽을 수 있습니다.

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

어려운 수동 형식을 피하는 더 좋은 방법이 있습니까?


70
왜 이것이 필요한지 궁금합니다.
해리슨

12
@Harrison 코드에서 들여 쓰기 된 방식에 따라 코드 출력을 들여 쓰려고했습니다.
Fab von Bellingshausen

14
진짜 질문은 : 왜 이것이 필요할까요? 들여 쓰기 수준은 정적입니다. 이 get_indentation_level()문장을 코드에 넣으면 확실하게 알 수 있습니다. 당신은 단지뿐만 아니라 할 수있는 print(3)직접 뭐든간에. 더 어려울 수있는 것은 함수 호출 스택의 현재 중첩 수준입니다.
tobias_k

19
코드 디버깅 목적입니까? 이것은 실행 흐름을 기록하는 매우 천재적 인 방법이거나 간단한 문제에 대해 과도하게 엔지니어링 된 솔루션과 같으며 어느 것이 확실하지는 않습니다 ... 어쩌면 둘 다입니다!
Blackhawk

7
@FabvonBellingshausen : 그것은 당신이 기대하는 것보다 훨씬 덜 읽기 쉬운 것처럼 들립니다. depth매개 변수 를 명시 적으로 전달 하고 다른 함수에 전달할 때 필요에 따라 적절한 값을 추가하면 더 나은 서비스를 제공받을 수 있다고 생각합니다 . 코드의 중첩은 출력에서 ​​원하는 들여 쓰기와 완전히 일치하지 않을 수 있습니다.
user2357112는

답변:


115

공백과 탭이 아닌 중첩 수준으로 들여 쓰기를 원하면 까다로워집니다. 예를 들어 다음 코드에서

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입니까? 의 측면에서 INDENT그리고 DEDENT형식적인 문법 토큰, 그것은 깊은 제로 수준입니다,하지만 당신은 같은 생각하지 않을 수 있습니다.


이 작업을 수행하려면 전체 파일을 호출 시점 및 카운트 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
get_nesting_level()함수 호출 내에서 호출 될 때 예상 한 방식으로 작동하지 않습니다 . 해당 함수 내에서 중첩 수준을 반환합니다. '글로벌'중첩 수준을 반환하도록 다시 작성할 수 있습니까?
Fab von Bellingshausen

11
@FabvonBellingshausen : 들여 쓰기 중첩 수준과 함수 호출 중첩 수준이 혼합되어있을 수 있습니다. 이 함수는 들여 쓰기 중첩 수준을 제공합니다. 함수 호출 중첩 수준은 다소 다르며 모든 예제에서 0 수준을 제공합니다. 당신이 들여 쓰기의 종류를 원한다면 / 중첩 수준 하이브리드를 호출하는 두 함수 호출 및 제어 흐름 구조 등에 증가 while하고 with행할 것,하지만 당신이 무엇을 요구하지, 그리고 질문을 변경하면이 시점에서 뭔가 다른 물어 나쁜 생각이 될 것입니다.
user2357112는

38
덧붙여서, 나는 이것이 정말로 이상한 일이라고 모든 사람들과 완전히 동의하고 있습니다. 아마도 당신이 해결하려는 문제를 해결하는 훨씬 더 좋은 방법이있을 것입니다. 이에 의존하면 모든 종류의 불쾌한 해킹을 사용하여 들여 쓰기 또는 함수 호출 구조를 변경하지 않아도되도록 강요함으로써 당신을 방해 할 것입니다 코드 변경.
user2357112는

4
나는 확실히 누군가가 실제로 이것에 대답 할 것이라고 기대하지 않았습니다. ( linecache이와 같은 것들에 대해서는 모듈을 고려하십시오 -트레이스 백을 인쇄하는 데 사용되며 zip 파일 및 기타 이상한 가져 오기 트릭에서 가져온 모듈을 처리 할 수 ​​있습니다)
Eevee

6
@Eevee : 나는 확실히 너무 많은 사람들이 기대하지 않은 upvote에 이! linecache파일 I / O의 양을 줄이는 데 도움이 될 수 있습니다 (그리고 나에게 상기시켜 주셔서 감사합니다). 그러나 그것을 최적화하기 시작하면 동일한 파일을 반복적으로 다시 토큰 화하는 방법에 의해 방해받을 것입니다. 동일한 통화 또는 동일한 파일 내의 여러 통화 사이트 우리가 그것을 최적화 할 수있는 여러 가지 방법이 있지만,이 미친 것을 얼마나 조정하고 방탄하고 싶은지 잘 모르겠습니다.
user2357112는

22

예, 확실히 가능합니다. 실제 사례는 다음과 같습니다.

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

4
@Prune이 작성한 주석과 관련하여 공백 대신 레벨로 들여 쓰기를 반환 할 수 있습니까? 단순히 4로 나누는 것이 항상 괜찮습니까?
Fab von Bellingshausen

2
아니요, 들여 쓰기 수준을 얻기 위해 4로 나누면이 코드에서 작동하지 않습니다. 마지막 인쇄 문장의 들여 쓰기 수준을 증가시켜 확인할 수 있으며, 마지막 인쇄 값이 증가합니다.
Craig Burgler

10
좋은 시작이지만 실제로 질문에 답하지는 않습니다. 공백 수는 들여 쓰기 수준과 다릅니다.
wim

1
그렇게 간단하지 않습니다. 4 개의 공백을 단일 공백으로 바꾸면 코드의 논리가 변경 될 수 있습니다.
wim

1
그러나이 코드는 OP가 찾고있는 것에 완벽합니다. (OP 주석 # 9) : '코드에서 들여 쓰기 방법에 따라 코드의 출력을 들여 쓰기를 원했습니다.' 그래서 그는 다음과 같이 할 수 있습니다print('{Space}'*get_indentation_level(), x)
Craig Burgler

10

sys.current_frame.f_lineno줄 번호를 얻기 위해 사용할 수 있습니다 . 그런 다음 들여 쓰기 수준의 수를 찾으려면 들여 쓰기가없는 이전 줄을 찾은 다음 해당 줄 번호에서 현재 줄 번호를 빼면 들여 쓰기 횟수가 표시됩니다.

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

질문은 공백이 아닌 들여 쓰기 수준의 수를 요구했습니다. 그것들은 반드시 비례적일 필요는 없습니다.
wim

데모 코드의 경우 출력은
1-2-3-3이어야합니다.

@CraigBurgler 1-2-3-3을 얻기 위해 :들여 쓰기가 0 인 줄이 나타날 때까지 현재 줄 앞의 줄 수를 셀 수 있습니다 . 편집을 확인하십시오!
Kasramvd

2
확인 ... 흠 ... 지금 user2357112의 테스트 케이스 @ 일부 시도)
크레이그 Burgler에게

@CraigBurgler이 솔루션은 대부분의 경우에 해당하며, 그 답변과 관련하여 일반적인 방법이므로 포괄적 인 솔루션도 제공하지 않습니다. 시도{3:4, \n 2:get_ind_num()}
Kasramvd

7

귀하의 질문으로 이어지는 "실제"문제를 해결하기 위해 indention 레벨을 추적 with하고 코드 의 블록 구조를 출력의 들여 쓰기 레벨과 일치 시키는 contextmanager를 구현할 수 있습니다. 이런 식으로 코드 들여 쓰기는 여전히 너무 많이 결합하지 않고 출력 들여 쓰기를 반영합니다. 코드를 다른 함수로 리팩터링하고 출력 들여 쓰기를 방해하지 않는 코드 구조를 기반으로 다른 들여 쓰기를 가질 수 있습니다.

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

6
>>> 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.

12
이것은 레벨이 아닌 공간에 들여 쓰기를 제공합니다. 프로그래머가 일관된 들여 쓰기 양을 사용하지 않으면 수준으로 변환하기가 어려울 수 있습니다.
Prune

4
기능이 문서화되어 있지 않습니까? 여기서
GingerPlusPlus
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.