その関数内から(トレースバックを使用せずに)関数名を決定する


479

Pythonでは、tracebackモジュールを使用せずに、その関数内から関数の名前を判別する方法はありますか?

関数barを持つモジュールfooがあるとします。を実行するときにfoo.bar()、バーがバーの名前を知る方法はありますか?またはもっと良いのは、foo.bar名前ですか?

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

6
なぜ受け入れられた回答がAndreas Young(またはそれを行う方法を示す他の回答)からの回答ではないのか、私には理解できません。代わりに、受け入れられた答えは「あなたはそれを行うことができない」ですが、それは間違っているようです。OPによる唯一の要件は、を使用しなかったことtracebackです。答えやコメントの時代でさえそれを裏付けるようには見えません。
sancho.s ReinstateMonicaCellio 2016

回答:


198

Pythonには、関数自体内で関数またはその名前にアクセスする機能がありません。提案れましたが却下されました。自分でスタックを操作したくない場合は、コンテキストを使用する"bar"か、bar.__name__依存する必要があります。

指定された拒否通知は次のとおりです。

このPEPは拒否されました。それがどのように実装されるべきか、または正確なセマンティクスがエッジケースでどのようにあるべきかは明確ではなく、与えられた十分な重要なユースケースがありません。応答はせいぜいぬるいです。


17
inspect.currentframe()そのような方法の1つです。
ユヴァル

41
@CamHartのアプローチと@Yuvalのアプローチを組み合わせることで、@ RoshOxymoronの回答の「非表示」および非推奨の可能性のあるメソッド、および@ neuro / @ AndreasJungの回答のスタックへの数値インデックス:print(inspect.currentframe().f_code.co_name)
ホブ

2
それが拒否された理由を要約することは可能ですか?
チャーリーパーカー

1
なぜこれが選ばれた答えなのですか?問題は、現在の関数やモジュール自体にアクセスすることではなく、名前だけです。そして、スタックトレース/デバッグ機能はすでにこの情報を持っています。
ヌレティン

410
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()
マイケル

7
inspect.currentframe().f_back.f_code.co_name装飾されたメソッドでは動作しませんが、inspect.stack()[0][3]動作します...
ダスティンワイアット

4
注意:inspect.stack()はパフォーマンスのオーバーヘッドが大きくなる可能性があるため、慎重に使用してください。私の腕の箱では、完了するのに240msかかりました(何らかの理由で)
gardarh

Pythonの再帰機構に存在する何かを使用してこれをより効率的に行うことができるように思えます
トムラッセル

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.25ms、1.24ms、0.5us、0.16us通常(非Pythonic :))秒(win7x64、python3.5.1)
Antony Hatchkins

@mbdevplがおかしいとは誰も思っていません:)-意味がなかったので、3回目の実行の出力の編集を送信しました。結果はしてきたかどうかを発想しないん0.100 usec0.199 usecけどいずれかの方法-高速化オプション1および2よりも数百倍、オプション4と同等(アントニーHatchkinsは3三倍速くオプション4以上のオプションを見つけましたが)。
dwanderson 2017年

すでにモジュールをインポートしているので、私は単にsys._getframe().f_code.co_nameover を使用し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。Pythonのクラス名と関数名は1つであり、それらを参照する変数は別のものであることを覚えておく価値があります。
Kos

ときどきFoo2()印刷したいかもしれませんFoo。次に例を示しますFoo2 = function_dict['Foo']; Foo2()。この場合、Foo2はおそらくコマンドラインパーサーの関数ポインターです。
Harvey

これにはどのような速度の影響がありますか?
ロバートC.バース、

2
何に関する速度の影響?ハードリアルタイムの状況などでこの情報が必要になる状況はありますか?
bgporter 14

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

コード内のさまざまな場所にあるログ文字列に関数名を入れたかったので、非常によく似たものが必要でした。おそらくそれを行うための最良の方法ではありませんが、現在の関数の名前を取得する方法は次のとおりです。


2
完全に機能し、sysを使用するだけで、追加のモジュールをロードする必要はありませんが、覚えるのは簡単ではありません:V
m3nda

@ erm3nda私の答えを見てください。
nerdfever.com 2018

1
@ nerdfever.com私の問題は、関数を作成することではなく、その関数の中に何を置くかを覚えていないことです。覚えるのは簡単ではないので、それを再構築するために常にいくつかのメモを見る必要があります。fフレームとcoコードを念頭に置いてください。私はそれをあまり使用しないので、それをいくつかのスニペットに保存するだけでよいです:-)
m3nda

24

私はこの便利なユーティリティを近くに置きます:

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

使用法:

myself()

1
ここで提案された代替案でこれはどのように行われますか?(出力がない作品ですん「<ラムダ>」;結果は遅くとも呼び出し時に、定義時に決定されたので、私は思ううーん:「sys._getframe()f_code.co_nameを自分はラムダ=」。。
NYCeyes

2
@ prismalytics.io:myself(myself())を呼び出し、その値(myself)だけを使用しない場合、探しているものが得られます。
ijw

NYCeyesは正しかった、名前はラムダの中で解決され、結果は<lambda>になります。sys._getframe()およびinspect.currentframe()メソッドは、名前を取得する関数内で直接実行する必要があります。インデックス1を指定できるため、inspect.stack()メソッドが機能します。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
デコレーターnoobとして、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を使用する必要があると思います。しかし、実行中の関数内で自分の関数を制御する関数の名前を取得しています...それは、美しいミームの始まりのように聞こえます:)
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()を使用するよりもこのバージョンの利点は、数千倍高速になることです[sys._getframe()の使用とinspect.stack()の使用に関するAlex Melihoffの投稿とタイミングを参照]。



11

これが将来を見据えたアプローチです。

@CamHartの提案と@Yuvalの提案を@RoshOxymoronの承認済みの回答と組み合わせると、回避する利点があります。

  • _hidden および潜在的に非推奨のメソッド
  • スタックへのインデックス付け(将来のpythonで並べ替えられる可能性があります)

したがって、これは将来のpythonバージョン(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:私の回答はそのまま、OPの質問への回答を示す例です
Douglas Denhartog

OK、my_functionはOPのランダムなユーザーの機能です。これは、デコレータに対する私の理解の欠如のせいです。どこ @?これは、引数を調整したくない関数に対してどのように機能しますか?ソリューションの理解方法:関数名を知りたい場合は、@ get_function_nameを追加して、name引数を追加する必要があります。これは、別の目的のために存在しないことを期待しています。何かが足りない可能性があります。申し訳ありません。
parvus

コメント内で自分のPythonコースを開始しない場合:1.関数はオブジェクトです。2.関数に名前属性を付加したり、名前を印刷/ログに記録したり、デコレータ内の「名前」を使用して任意のことを実行したりできます。3.デコレータは複数の方法でアタッチできます(例:@または私の例)。4.デコレータは@wrapsを使用したり、クラス自体になったりできます。5.続行できますが、プログラミングは楽しいです!
Douglas Denhartog

これ__name__は、関数の属性に到達するための複雑な方法のように見えます。使用法は、取得しようとしているものを知ることを必要とします。これは、関数がその場で定義されていない単純なケースでは、私にはあまり役に立ちません。
Avi

3

多重継承シナリオ内で安全にスーパーを呼び出すために使用する独自のアプローチを実行します(すべてのコードを配置します)

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.