해당 함수 내에서 함수 이름 결정 (트레이스 백을 사용하지 않고)


479

파이썬에서는 traceback모듈 을 사용하지 않고 해당 함수 내에서 함수 이름을 결정하는 방법이 있습니까?

기능 표시 줄이있는 foo 모듈이 있다고 가정 해보십시오. 를 실행할 때 foo.bar()bar가 bar의 이름을 알 수있는 방법이 있습니까? 아니면 더 나은 foo.bar이름입니까?

#foo.py  
def bar():
    print "my name is", __myname__ # <== how do I calculate this at runtime?

6
나는 왜 안드레아스 영 (또는 그것을 수행하는 방법을 보여주는 다른 사람들)의 대답이 받아 들여지지 않는지 이해하지 못합니다. 대신 받아 들여지는 대답은 "그렇게 할 수 없습니다"입니다. OP의 유일한 요구 사항은을 사용하지 않는 것 traceback입니다. 답과 의견의 시대조차도 그것을 뒷받침하지 않는 것 같습니다.
sancho.s ReinstateMonicaCellio

답변:


198

파이썬에는 함수 자체 내에서 함수 또는 그 이름에 액세스하는 기능이 없습니다. 제안 되었지만 거부 되었습니다 . 스택을 직접 사용하고 싶지 않은 경우 컨텍스트에 따라 사용 "bar"하거나 사용해야합니다 bar.__name__.

주어진 거부 통지는 다음과 같습니다.

이 PEP는 거부됩니다. 어떻게 구현해야하는지 또는 정확한 경우 의미론이 무엇인지 명확하지 않으며, 중요한 사용 사례가 충분하지 않습니다. 응답은 기껏해야 미지근했습니다.


17
inspect.currentframe()그런 방법 중 하나입니다.
Yuval

41
: Yuval 교수의 피합니다 "숨겨진"와 @ 신경 / @ AndreasJung의 답변을 스택에 RoshOxymoron의 대답뿐만 아니라 숫자 색인 @ 잠재적으로 사용되지 않는 방법을 @로 CamHart의 접근 @ 결합print(inspect.currentframe().f_code.co_name)
호브

2
거부 된 이유를 요약 할 수 있습니까?
Charlie Parker

1
왜 이것이 정답입니까? 질문은 현재 기능이나 모듈 자체에 액세스하는 것이 아니라 이름 만입니다. 그리고 스택 추적 / 디버깅 기능에는 이미이 정보가 있습니다.
nurettin

409
import inspect

def foo():
   print(inspect.stack()[0][3])
   print(inspect.stack()[1][3]) #will give the caller of foos name, if something called foo

47
[1][3]발신자의 이름을 얻을 수도 있기 때문에 좋습니다 .
Kos

57
print(inspect.currentframe().f_code.co_name)또는을 사용 하여 발신자의 이름을 얻을 수도 있습니다 print(inspect.currentframe().f_back.f_code.co_name). 모든 스택 프레임 목록을 검색하지 않기 때문에 더 빠를 것이라고 생각합니다 inspect.stack().
Michael

7
inspect.currentframe().f_back.f_code.co_name반면이 장식 된 방법으로 작동하지 않습니다 inspect.stack()[0][3]않습니다 ...
더스틴 와이어트

4
참고 : inspect.stack ()은 성능 오버 헤드가 발생할 수 있으므로 조금만 사용하십시오! 내 팔 상자에 그것은 (어떤 이유로) 완료하는 데 240ms했다
gardarh

파이썬 재귀 기계에 존재하는 것이 더 효율적으로 수행 될 수있는 것처럼 보입니다
Tom Russell

160

동일한 결과를 얻는 몇 가지 방법이 있습니다.

from __future__ import print_function
import sys
import inspect

def what_is_my_name():
    print(inspect.stack()[0][0].f_code.co_name)
    print(inspect.stack()[0][3])
    print(inspect.currentframe().f_code.co_name)
    print(sys._getframe().f_code.co_name)

inspect.stack통화는 다른 통화보다 수천 배 느립니다.

$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop

5
inspect.currentframe()실행 시간과 개인 멤버의 사용 사이에 좋은 트레이드 오프 인 것 같습니다
FabienAndre

1
@mbdevpl 내 번호는 이에 따라의 1.25 ms, 1.24ms, 0.5us의, 0.16us 일반 (nonpythonic :)) 초 (win7x64, python3.5.1)이다
안토니 Hatchkins

아무도 생각하지 않습니다. @ mbdevpl은 미쳤다고 생각합니다. 어떤 결과가있었습니다할지 생각이 0.100 usec0.199 usec있지만, 어느 쪽이든 - 빠른 (안토니 Hatchkins 옵션 4보다 옵션 3 세 배 빠른 발견하지만) 옵션 4와 비교 옵션 1, 2보다 수백 배.
dwanderson

내가 사용하는 sys._getframe().f_code.co_name동안 inspect.currentframe().f_code.co_name나는 이미 가져온 간단하기 때문에 sys모듈을. 합리적인 결정입니까? (속도를 고려하면 상당히 비슷 함)
PatrickT

47

@Andreas Jung이 보여주는 접근 방식을 사용하여 정의 된 이름을 얻을 수 있지만 함수가 호출 된 이름이 아닐 수도 있습니다.

import inspect

def Foo():
   print inspect.stack()[0][3]

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

그 구별이 당신에게 중요한지 아닌지는 말할 수 없습니다.


3
와 같은 상황입니다 .func_name. 파이썬에서 클래스 이름과 함수 이름은 기억해야 할 것이 있고 그것들을 참조하는 변수는 다른 것입니다.
Kos

때로는 Foo2()인쇄 하고 싶을 수도 있습니다 Foo. 예를 들면 다음과 같습니다 Foo2 = function_dict['Foo']; Foo2().. 이 경우 Foo2는 아마도 명령 행 구문 분석기의 함수 포인터입니다.
Harvey

이것이 어떤 속도 영향을 미칩니 까?
Robert C. Barth

2
무엇에 대한 속도 영향? 어려운 실시간 상황이나 그 밖의 상황에서이 정보가 필요한 상황이 있습니까?
bgporter

44
functionNameAsString = sys._getframe().f_code.co_name

함수 이름을 코드의 여러 위치에있는 로그 문자열에 넣고 싶었 기 때문에 매우 비슷한 것을 원했습니다. 아마도 가장 좋은 방법은 아니지만 현재 함수의 이름을 얻는 방법이 있습니다.


2
sys를 사용하여 완전히 작동하면 더 많은 모듈을로드 할 필요는 없지만 기억하기 쉽지 않습니다 : V
m3nda

@ erm3nda 내 답변을 참조하십시오.
nerdfever.com

1
@ nerdfever.com 내 문제는 함수를 만들려는 것이 아니라 해당 함수에 넣을 내용을 기억하지 않는 것입니다. 기억하기 쉽지 않으므로 항상 다시 작성하기 위해 메모를해야합니다. f프레임과 co코드 를 염두 에 두겠습니다 . 난 그냥 일부 발췌 문장에서 저장하면 더 잘 사용하지 않습니다 :-)
m3nda

24

이 편리한 유틸리티를 근처에 둡니다.

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

용법:

myself()

1
여기에 제안 된 대안으로 어떻게이 작업을 수행 할 수 있습니까? "myself = lambda : sys._getframe (). f_code.co_name"이 작동하지 않습니다 (출력은 "<lambda>"입니다. 결과는 나중에 호출 시간이 아니라 정의 시간에 결정되기 때문에 생각합니다.
NYCeyes

2
@ prismalytics.io : 나 자신 (myself ())을 호출하고 그 값 (myself) 만 사용하지 않으면 원하는 것을 얻을 수 있습니다.
ijw

NYCeyes가 옳았습니다. 이름은 람다에서 확인되므로 결과는 <lambda>입니다. sys._getframe () 및 inspect.currentframe () 메소드는 이름을 얻으려는 함수 내에서 직접 실행해야합니다. inspect.stack () 메소드는 인덱스 1을 지정할 수 있기 때문에 작동합니다. inspect.stack () [0] [3]을 수행하면 <lambda>도 생성됩니다.
kikones34

20

inspect가장 좋은 방법 이라고 생각 합니다. 예를 들면 다음과 같습니다.

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])

17

함수 이름을 쓸 래퍼를 찾았습니다.

from functools import wraps

def tmp_wrap(func):
    @wraps(func)
    def tmp(*args, **kwargs):
        print func.__name__
        return func(*args, **kwargs)
    return tmp

@tmp_wrap
def my_funky_name():
    print "STUB"

my_funky_name()

이것은 인쇄됩니다

my_funky_name

그루터기


1
데코레이터 멍청한 놈으로서, my_funky_name의 컨텍스트 내에서 func .__ name__에 액세스 할 수있는 방법이 있는지 궁금합니다 (따라서 값을 검색하여 my_funky_name 내에서 사용할 수 있음)
cowbert

my_funky_name 함수 내에서 수행하는 방법은 my_funky_name.__name__입니다. func .__ name__을 새 매개 변수로 함수에 전달할 수 있습니다. func (* args, ** kwargs, my_name = func .__ name__). 함수 내부에서 데코레이터 이름을 얻으려면 inspect를 사용해야한다고 생각합니다. 그러나 내 실행중인 기능 내에서 내 기능을 제어하는 ​​기능의 이름을 얻는 것 ... ... 아름다운 meme의 시작과 같은 소리 :)
cad106uk

15

이것은 실제로 질문에 대한 다른 답변에서 파생됩니다.

여기 내 테이크가있다 :

import sys

# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


def testFunction():
    print "You are in function:", currentFuncName()
    print "This function's caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

inspect.stack ()을 사용하는 것보다이 버전의 장점은 수천 배나 빨라야한다는 것입니다 [apps.stack () 사용과 비교하여 Alex Melihoff의 포스트 및 sys._getframe () 사용에 대한 타이밍 참조].



11

미래에 대비할 수있는 방법이 있습니다.

@CamHart와 @Yuval의 제안을 @RoshOxymoron의 승인 된 답변 과 결합하면 다음을 피할 수 있습니다.

  • _hidden 잠재적으로 사용되지 않는 방법
  • 스택으로 인덱싱 (향후 파이썬에서 재정렬 될 수 있음)

그래서 이것은 미래의 파이썬 버전 (2.7.3 및 3.3.2에서 테스트 됨)에서 훌륭하게 작동한다고 생각합니다.

from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

11
import sys

def func_name():
    """
    :return: name of caller
    """
    return sys._getframe(1).f_code.co_name

class A(object):
    def __init__(self):
        pass
    def test_class_func_name(self):
        print(func_name())

def test_func_name():
    print(func_name())

테스트:

a = A()
a.test_class_func_name()
test_func_name()

산출:

test_class_func_name
test_func_name

11

사람들이 왜 그것을 복잡하게하는지 모르겠습니다.

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))

아마도 외부에서 sys 모듈의 개인 기능을 사용하고 있기 때문일 수 있습니다. 일반적으로 그것은 나쁜 습관으로 간주됩니까?
tikej

@tikej, 사용법은 여기에 명시된 모범 사례에 동의합니다. docs.quantifiedcode.com/python-anti-patterns/correctness/…
r

10
import inspect

def whoami():
    return inspect.stack()[1][3]

def whosdaddy():
    return inspect.stack()[2][3]

def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
    bar()

def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())

foo()
bar()

IDE에서 코드 출력

안녕, 난 바보 야 아빠는

안녕, 난 바야, 아빠는 바보 야

안녕, 난 바야, 아빠는


8

데코레이터를 사용할 수 있습니다 :

def my_function(name=None):
    return name

def get_function_name(function):
    return function(name=function.__name__)

>>> get_function_name(my_function)
'my_function'

그것이 포스터의 질문에 어떻게 대답 하는가? 함수 내에서 함수 이름이 알려진 방법의 예를 포함하도록 이것을 확장 할 수 있습니까?
parvus

@parvus : 내 대답 입니다으로는 영업 이익의 질문에 대한 답을 보여주는 예입니다
더글러스 Denhartog

좋아, my_function은 임의 사용자의 OP 기능입니다. 이것을 데코레이터에 대한 이해 부족으로 비난하십시오. @는 어디에? 인수를 적용하지 않으려는 함수에 대해서는 어떻게 작동합니까? 솔루션을 이해하는 방법 : 함수 이름을 알고 싶을 때 @get_function_name으로 추가하고 name 인수를 추가하여 다른 목적으로 아직 존재하지 않기를 바랍니다. 뭔가 빠진 것 같습니다. 죄송합니다.
parvus

주석 내에서 파이썬 코스를 시작하지 않고 : 1. 함수는 객체입니다. 2. 이름 속성을 함수에 첨부하거나, 이름을 인쇄 / 기록하거나, 데코레이터 내부에 "이름"으로 여러 가지 작업을 수행 할 수 있습니다. 3. 데코레이터는 여러 방법으로 연결될 수 있습니다 (예 : @ 또는 내 예). 4. 데코레이터는 @wrap을 사용하거나 클래스 자체가 될 수 있습니다. 5. 나는 계속할 수 있지만, 행복한 프로그래밍!
Douglas Denhartog

이것은 __name__함수 의 속성 을 얻는 복잡한 방법처럼 보입니다 . 사용법은 얻고 자하는 것을 알아야합니다. 기능이 즉시 정의되지 않은 간단한 경우에는 나에게 유용하지 않습니다.
Avi

3

다중 상속 시나리오 내에서 안전을 사용하여 super를 호출하는 데 사용되는 자체 접근법을 사용합니다 (모든 코드를 넣습니다)

def safe_super(_class, _inst):
    """safe super call"""
    try:
        return getattr(super(_class, _inst), _inst.__fname__)
    except:
        return (lambda *x,**kx: None)


def with_name(function):
    def wrap(self, *args, **kwargs):
        self.__fname__ = function.__name__
        return function(self, *args, **kwargs)
return wrap

샘플 사용법 :

class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A\n'
        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B\n'
        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C\n'
        safe_super(C, self)()

그것을 테스트 :

a = C()
a.test()

산출:

called from C
called from A
called from B

각 @with_name 장식 메소드 내에서 현재 함수 이름으로 self .__ fname__에 액세스 할 수 있습니다.


3

최근에 위의 답변을 사용하여 해당 함수의 컨텍스트에서 함수의 docstring에 액세스하려고 시도했지만 위의 질문은 이름 문자열 만 반환했기 때문에 작동하지 않았습니다.

다행히도 간단한 해결책을 찾았습니다. 나처럼, 단순히 함수 이름의 문자열에 eval ()을 적용 할 수있는 이름을 나타내는 문자열을 가져 오는 대신 함수를 참조하려고합니다.

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)

3

스택 요소에 의존하지 않는 것이 좋습니다. 다른 컨텍스트에서 코드를 사용하는 경우 (예 : Python 인터프리터) 스택이 변경되어 인덱스가 손상됩니다 ([0] [3]).

나는 당신에게 그런 것을 제안합니다 :

class MyClass:

    def __init__(self):
        self.function_name = None

    def _Handler(self, **kwargs):
        print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
        self.function_name = None

    def __getattr__(self, attr):
        self.function_name = attr
        return self._Handler


mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')

0

이것은 데코레이터로 달성하기가 매우 쉽습니다.

>>> from functools import wraps

>>> def named(func):
...     @wraps(func)
...     def _(*args, **kwargs):
...         return func(func.__name__, *args, **kwargs)
...     return _
... 

>>> @named
... def my_func(name, something_else):
...     return name, something_else
... 

>>> my_func('hello, world')
('my_func', 'hello, world')
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.