특정 함수가 파이썬의 스택에 있는지 여부를 결정하는 효율적인 방법

StackOverflow https://stackoverflow.com/questions/1403471

  •  05-07-2019
  •  | 
  •  

문제

디버깅의 경우 호출 스택에서 특정 기능이 더 높는지 여부를 알리는 것이 종종 유용합니다. 예를 들어, 우리는 종종 특정 함수가 우리에게 전화 할 때 디버깅 코드 만 실행하려고합니다.

한 가지 해결책은 모든 스택 항목을 더 높게 검사하는 것이지만, 이것은 스택에 깊고 반복적으로 호출되는 함수에 있습니다. 이것은 과도한 오버 헤드로 이어집니다. 문제는 합리적으로 효율적인 방식으로 특정 함수가 통화 스택에서 더 높은지 확인할 수있는 방법을 찾는 것입니다.

비슷한

도움이 되었습니까?

해결책

당신이 목표로하는 기능이 "스택에서 한 인스턴스의 인스턴스가 활성화된다"(iow : 기능이 깨끗하고 만질 수없고 당신의 독특한 필요를 알 수 없다면). , 상단 (그리고 함수가 없음) 또는 관심있는 기능을위한 스택 프레임을 누르기 전까지는 스택에 프레임 위로 워킹 프레임에 대한 대안이 없습니다. 이 질문에 대한 몇 가지 의견이 알 수 있듯이,이를 최적화하기 위해 노력할 가치가 있는지 의심의 여지가 있습니다. 그러나 논쟁을 위해 ~였다 할 보람 있는...:

편집하다: OP의 원래 답변에는 많은 결함이 있었지만 일부는 수정되었으므로 현재 상황과 특정 측면이 중요한 이유를 반영하기 위해 편집하고 있습니다.

우선, 사용하는 것이 중요합니다 try/except, 또는 with, 데코레이터에서 모니터링되는 함수의 출구가 정상적인 것뿐만 아니라 OP의 원래 버전의 원래 버전이 한 것처럼) 적절하게 설명됩니다.

둘째, 모든 데코레이터는 장식 된 기능을 유지해야합니다. __name__ 그리고 __doc__ 손상되지 않습니다. 그게 전부입니다 functools.wraps (다른 방법이 있지만 wraps 가장 간단하게 만듭니다).

셋째, 첫 번째 요점만큼 중요합니다. set, OP가 원래 선택한 데이터 구조 인 잘못된 선택은 잘못된 선택입니다. 함수는 여러 번 스택에있을 수 있습니다 (직접 또는 간접 재귀). 우리는 분명히 "멀티 세트"( "백"이라고도 함)가 필요합니다.이 구조는 각 항목이 존재하는 "수"를 추적하는 세트와 같은 구조가 필요합니다. 파이썬에서 멀티 세트의 자연스러운 구현은 카운트에 대한 DICT 매핑 키입니다. collections.defaultdict(int).

넷째, 일반적인 접근법은 threadSafe입니다 (적어도 ;-). 다행스럽게도, threading.local 적용 할 수있는 경우 사소하게 만들고 여기에서 반드시 (각 스택은 별도의 통화 스레드를 갖는 각 스택)해야합니다.

다섯째, 일부 의견에서 브로치 된 흥미로운 문제 (일부 답변에서 제공된 데코레이터가 다른 데코레이터와 얼마나 나쁘게 재생되는지 주목하십시오. 모니터링 데코레이터가 마지막으로 (가장 바깥 쪽) 하나 인 것 같습니다. 그렇지 않으면 검사 휴식이 나옵니다. 기능 객체 자체를 모니터링 딕의 키로 사용하는 자연 스럽지만 불행한 선택.

나는 다른 선택으로 이것을 해결할 것을 제안합니다. 데코레이터를 가져 가게하십시오 (문자열, 예 : identifier 고유 해야하는 인수 (각 스레드에서) 및 식별자를 모니터링 DITT의 키로 사용하십시오. 스택을 확인하는 코드는 물론 식별자를 알고 있어야합니다.

장식 시간에 데코레이터는 고유성 속성을 확인할 수 있습니다 (별도의 세트를 사용하여). 식별자는 함수 이름으로 기본값으로 남겨질 수 있습니다 (따라서 동일한 네임 스페이스에서 동종 기능을 모니터링하는 유연성을 유지하는 것이 명시 적으로 만 필요합니다). 고유성 속성은 모니터링 목적으로 여러 모니터링 기능을 "동일"으로 간주 할 때 명시 적으로 포기할 수 있습니다 (주어진 경우 경우에 발생할 수 있습니다. def 진술은 프로그래머가 모니터링 목적으로 "동일한 함수"를 고려하려는 여러 기능 개체를 만들기 위해 약간 다른 컨텍스트에서 여러 번 실행해야합니다). 마지막으로, 추가 장식이 불가능한 것으로 알려진 드문 경우에 대해 "기능 객체로 식별자로"선택적으로 되돌릴 수 있어야합니다 (이 경우 고유성을 보장하는 가장 쉬운 방법 일 수 있기 때문입니다).

그래서이 많은 고려 사항을 정리하면 threadlocal_var 물론 도구 상자 모듈에 이미있을 수있는 유틸리티 기능 ;-) 다음과 같은 것 ... :

import collections
import functools
import threading

threadlocal = threading.local()

def threadlocal_var(varname, factory, *a, **k):
  v = getattr(threadlocal, varname, None)
  if v is None:
    v = factory(*a, **k)
    setattr(threadlocal, varname, v)
  return v

def monitoring(identifier=None, unique=True, use_function=False):
  def inner(f):
    assert (not use_function) or (identifier is None)
    if identifier is None:
      if use_function:
        identifier = f
      else:
        identifier = f.__name__
    if unique:
      monitored = threadlocal_var('uniques', set)
      if identifier in monitored:
        raise ValueError('Duplicate monitoring identifier %r' % identifier)
      monitored.add(identifier)
    counts = threadlocal_var('counts', collections.defaultdict, int)
    @functools.wraps(f)
    def wrapper(*a, **k):
      counts[identifier] += 1
      try:
        return f(*a, **k)
      finally:
        counts[identifier] -= 1
    return wrapper
  return inner

이 코드를 테스트하지 않았으므로 오타 등이 포함되어있을 수 있지만 위에서 설명한 모든 중요한 기술 포인트를 다루기를 바랍니다.

그만한 가치가 있습니까? 아마도 이전에 설명했듯이. 그러나 나는 "만약 할 가치가 있다면 올바른 할 가치가있다"는 선을 따라 생각합니다. ;-).

다른 팁

나는이 접근법이 마음에 들지 않지만 여기에 당신이하고있는 일의 고정 버전이 있습니다.

from collections import defaultdict
import threading
functions_on_stack = threading.local()

def record_function_on_stack(f):
    def wrapped(*args, **kwargs):
        if not getattr(functions_on_stack, "stacks", None):
            functions_on_stack.stacks = defaultdict(int)
        functions_on_stack.stacks[wrapped] += 1

        try:
            result = f(*args, **kwargs)
        finally:
            functions_on_stack.stacks[wrapped] -= 1
            if functions_on_stack.stacks[wrapped] == 0:
                del functions_on_stack.stacks[wrapped]
        return result

    wrapped.orig_func = f
    return wrapped

def function_is_on_stack(f):
    return f in functions_on_stack.stacks

def nested():
    if function_is_on_stack(test):
        print "nested"

@record_function_on_stack
def test():
    nested()

test()

이것은 재귀, 스레딩 및 예외를 처리합니다.

나는 두 가지 이유로이 접근법이 마음에 들지 않습니다.

  • 기능이 더 장식되어 있으면 작동하지 않습니다. 이것이 최종 데코레이터 여야합니다.
  • 디버깅에 이것을 사용하는 경우 두 곳에서 코드를 편집해야합니다. 하나는 데코레이터를 추가하고 하나는 그것을 사용합니다. 스택을 검사하는 것이 훨씬 편리하므로 디버깅하는 코드에서 코드를 편집하면됩니다.

더 나은 접근 방식은 스택을 직접 검사하는 것입니다 (아마도 속도를위한 기본 확장으로). 가능하면 스택 프레임의 수명에 대한 결과를 캐시하는 방법을 찾는 것입니다. (그러나 파이썬 코어를 수정하지 않고 가능한지 잘 모르겠습니다.)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top