호출 된 메소드에서 호출자의 메소드 이름을 얻는 방법은 무엇입니까?


187

파이썬 : 호출 된 메소드에서 호출자의 메소드 이름을 얻는 방법은 무엇입니까?

두 가지 방법이 있다고 가정합니다.

def method1(self):
    ...
    a = A.method2()

def method2(self):
    ...

method1에 대한 변경을 원하지 않으면 method2에서 호출자의 이름을 얻는 방법 (이 예제에서 이름은 method1입니다)?


3
예. 이제 일부 문서를 생성하고 테스트 전용으로 만들고 싶습니다.
zs2020

기술은 하나이고 방법론은 또 다른 것입니다.
zs2020

답변:


233

inspect.getframeinfo 및 기타 관련 기능 inspect이 도움 이 될 수 있습니다.

>>> import inspect
>>> def f1(): f2()
... 
>>> def f2():
...   curframe = inspect.currentframe()
...   calframe = inspect.getouterframes(curframe, 2)
...   print('caller name:', calframe[1][3])
... 
>>> f1()
caller name: f1

이 내부 검사는 디버깅 및 개발을 돕기위한 것입니다. 생산 기능 목적으로 사용하는 것은 좋지 않습니다.


18
"제작 기능을 목적으로하는 것이 바람직하지 않습니다." 왜 안돼?
beltsonata

25
@beltsonata CPython 구현에 의존하므로 PyPy 또는 Jython 또는 다른 런타임을 사용하려고 시도하면 이것을 사용하는 코드가 중단됩니다. 로컬에서 개발하고 디버깅하지만 프로덕션 시스템에서 실제로 원하는 것은 아닙니다.
robru

@EugeneKrevenets 파이썬 버전 외에도, 한 번 소개 된 몇 분 안에 두 번째 실행으로 실행되는 코드를 만드는 문제가 발생했습니다. 그것은 비효율적이다
Dmitry

이것이 python3 문서에 언급되지 않은 이유는 무엇입니까? docs.python.org/3/library/inspect.html 그들이 옳지 않다면 적어도 경고를했을까요?

1
이것이 성능에 큰 타격이라는 것은 부끄러운 일입니다. 로깅은 이와 같은 (또는 CallerMemberName ) 훨씬 더 좋을 수 있습니다 .
StingyJack

92

더 짧은 버전 :

import inspect

def f1(): f2()

def f2():
    print 'caller name:', inspect.stack()[1][3]

f1()

(@Alex 및 Stefaan Lippen 덕분에 )


안녕하세요, 내가 실행할 때 아래 오류가 발생합니다. "/usr/lib/python2.7/inspect.py"파일, 528 행, sourcesource 및 file [0] + file [-1]! = '이 아닌 경우 findsource에서 <> ': IndexError : 문자열 인덱스가 범위를 벗어남 제안을 제공 할 수 있습니까? 미리 감사드립니다.
Pooja

'KeyError를 :이 방법은 나에게 오류 준 주요 '
프락시 텔레스

61

이것은 잘 작동하는 것 같습니다 :

import sys
print sys._getframe().f_back.f_code.co_name

1
이보다 훨씬 빠른 것 같습니다inspect.stack
kentwait

여전히 보호 된 멤버를 사용하는데, 이는 일반적으로 권장되지 않습니다. sys모듈이 약간의 리팩토링 후에 실패 할 수 있기 때문 입니다.
filiprem

29

모듈과 클래스를 포함한 전체 메소드 이름을 작성하려고 시도하는 약간 더 긴 버전을 생각해 냈습니다.

https://gist.github.com/2151727 (rev 9cccbf)

# Public Domain, i.e. feel free to copy/paste
# Considered a hack in Python 2

import inspect

def caller_name(skip=2):
    """Get a name of a caller in the format module.class.method

       `skip` specifies how many levels of stack to skip while getting caller
       name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.

       An empty string is returned if skipped levels exceed stack height
    """
    stack = inspect.stack()
    start = 0 + skip
    if len(stack) < start + 1:
      return ''
    parentframe = stack[start][0]    

    name = []
    module = inspect.getmodule(parentframe)
    # `modname` can be None when frame is executed directly in console
    # TODO(techtonik): consider using __main__
    if module:
        name.append(module.__name__)
    # detect classname
    if 'self' in parentframe.f_locals:
        # I don't know any way to detect call from the object method
        # XXX: there seems to be no way to detect static method call - it will
        #      be just a function call
        name.append(parentframe.f_locals['self'].__class__.__name__)
    codename = parentframe.f_code.co_name
    if codename != '<module>':  # top level usually
        name.append( codename ) # function or a method

    ## Avoid circular refs and frame leaks
    #  https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack
    del parentframe, stack

    return ".".join(name)

놀랍게도, 이것은 많은 다른 장소에서 호출 될 수있는 로깅 코드에서 잘 작동했습니다. 매우 감사합니다.
little_birdie

1
또한 삭제하지 않으면 inspect-docs에stack 설명 된대로
원주

@ankostis 증명할 테스트 코드가 있습니까?
anatoly techtonik

1
코멘트에 표시하기 어려움 ... 편집기에서이 구동 코드 (메모리에서 입력)를 복사하여 붙여 넣기하고 두 버전의 코드를 모두 시도해보십시오.```import weakref class C : pass def kill () : print ( 'Killed' ) def leaking () : caller_name () local_var = C () weakref.finalize (local_var, kill) leaking () print ( "Local_var가 죽었어야합니다")```다음을 얻어야합니다.```Killed Local_var이 ``사망`이 아니라```Local_var 살해```살해되어 있어야합니다
ankostis

1
대박! 다른 솔루션이 실패했을 때 작동했습니다! 클래스 메서드와 람다 식을 사용하므로 까다 롭습니다.
osa

17

사용 inspect.currentframe().f_back.f_code.co_name합니다. 그 사용은 주로 세 가지 유형 중 하나 인 이전 답변에서 다루지 않았습니다.

  • 일부 이전 답변은 사용 inspect.stack하지만 너무 느립니다 .
  • 일부 선행 답변 sys._getframe은 밑줄이 주어지면 내부 개인 기능을 사용하므로 암시 적으로 사용하지 않는 것이 좋습니다.
  • 하나의 이전 답변이 사용 inspect.getouterframes(inspect.currentframe(), 2)[1][3]되지만 무엇 [1][3]을 액세스 하는지 는 확실하지 않습니다 .
import inspect
from types import FrameType
from typing import cast


def caller_name() -> str:
    """Return the calling function's name."""
    # Ref: https://stackoverflow.com/a/57712700/
    return cast(FrameType, cast(FrameType, inspect.currentframe()).f_back).f_code.co_name


if __name__ == '__main__':
    def _test_caller_name() -> None:
        assert caller_name() == '_test_caller_name'
    _test_caller_name()

참고 cast(FrameType, frame)충족하는 데 사용됩니다 mypy.


승인 : 답변에 대한 1313e의 사전 의견 .


10

위의 것들의 융합의 비트. 그러나 여기에 내 균열이 있습니다.

def print_caller_name(stack_size=3):
    def wrapper(fn):
        def inner(*args, **kwargs):
            import inspect
            stack = inspect.stack()

            modules = [(index, inspect.getmodule(stack[index][0]))
                       for index in reversed(range(1, stack_size))]
            module_name_lengths = [len(module.__name__)
                                   for _, module in modules]

            s = '{index:>5} : {module:^%i} : {name}' % (max(module_name_lengths) + 4)
            callers = ['',
                       s.format(index='level', module='module', name='name'),
                       '-' * 50]

            for index, module in modules:
                callers.append(s.format(index=index,
                                        module=module.__name__,
                                        name=stack[index][3]))

            callers.append(s.format(index=0,
                                    module=fn.__module__,
                                    name=fn.__name__))
            callers.append('')
            print('\n'.join(callers))

            fn(*args, **kwargs)
        return inner
    return wrapper

사용하다:

@print_caller_name(4)
def foo():
    return 'foobar'

def bar():
    return foo()

def baz():
    return bar()

def fizz():
    return baz()

fizz()

출력은

level :             module             : name
--------------------------------------------------
    3 :              None              : fizz
    2 :              None              : baz
    1 :              None              : bar
    0 :            __main__            : foo

2
요청 된 스택 깊이가 실제보다 큰 경우 IndexError가 발생합니다. modules = [(index, inspect.getmodule(stack[index][0])) for index in reversed(range(1, min(stack_size, len(inspect.stack()))))]모듈을 얻는 데 사용 합니다.
jake77

1

클래스를 살펴보고 메소드가 속한 클래스와 메소드를 원할 경우 방법을 찾았습니다. 약간의 추출 작업이 필요하지만 그 점을 지적합니다. 이것은 Python 2.7.13에서 작동합니다.

import inspect, os

class ClassOne:
    def method1(self):
        classtwoObj.method2()

class ClassTwo:
    def method2(self):
        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 4)
        print '\nI was called from', calframe[1][3], \
        'in', calframe[1][4][0][6: -2]

# create objects to access class methods
classoneObj = ClassOne()
classtwoObj = ClassTwo()

# start the program
os.system('cls')
classoneObj.method1()

0
#!/usr/bin/env python
import inspect

called=lambda: inspect.stack()[1][3]

def caller1():
    print "inside: ",called()

def caller2():
    print "inside: ",called()

if __name__=='__main__':
    caller1()
    caller2()
shahid@shahid-VirtualBox:~/Documents$ python test_func.py 
inside:  caller1
inside:  caller2
shahid@shahid-VirtualBox:~/Documents$
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.