Çağrılan yöntemde arayanın yöntem adı nasıl alınır?


188

Python: Çağıran yöntemde arayanın yöntem adı nasıl alınır?

2 yöntemim olduğunu varsayın:

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

def method2(self):
    ...

Yöntem1 için herhangi bir değişiklik yapmak istemiyorsam, yöntem2'de arayanın adını (bu örnekte, adı method1) nasıl alabilirim?


3
Evet. Şimdi sadece bazı dokümantasyon malzemeleri oluşturmak ve sadece test etmek istiyorum.
zs2020

Teknoloji bir şeydir, metodoloji başka bir şeydir.
zs2020

Yanıtlar:


233

inspect.getframeinfo ve diğer ilgili işlevler inspectyardımcı olabilir:

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

bu içgözlem, hata ayıklama ve geliştirmeye yardımcı olmayı amaçlamaktadır; üretim işlevselliği için ona güvenmeniz önerilmez.


18
"Üretim işlevselliği amaçları için ona güvenmeniz önerilmez." Neden olmasın?
beltsonata

25
@beltsonata CPython uygulamasına bağlıdır, bu nedenle PyPy veya Jython veya diğer çalışma zamanlarını kullanmaya çalıştığınızda bunu kullanan kod kırılır. sadece yerel olarak geliştirme ve hata ayıklama yapıyorsanız, ancak üretim sisteminizde gerçekten istediğiniz bir şey değilse sorun değil.
robru

@EugeneKrevenets Sadece python sürümünün ötesinde, tanıtıldıktan sonra birkaç dakika içinde ikinci bir çalışma altında çalışan kodu yaptığı bir sorunla karşılaştım. fena halde verimsiz
Dmitry

Bu neden python3 belgelerinde belirtilmiyor? docs.python.org/3/library/inspect.html En azından kötü ise bir uyarı yaparlar mı?

1
Bu performans üzerinde bir hit olduğu için bir utanç. Günlüğe kaydetme böyle bir şeyle (veya CallerMemberName ) çok daha hoş olabilir .
StingyJack

93

Kısa versiyon:

import inspect

def f1(): f2()

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

f1()

(@Alex ve Stefaan Lippen sayesinde )


Merhaba, bunu çalıştırdığımda hata alıyorum: Dosya /usr/lib/python2.7/inspect.py ", satır 528, kaynak dosyası değilse findsource ve dosya [0] + dosya [-1]! = ' <> ': IndexError: dize dizini aralık dışında U öneride bulunabilir misiniz? Thanx önceden.
Pooja

Bu yaklaşım bana bir hata verdi: KeyError: ' ana '
Praxiteles

61

Bu iyi çalışıyor gibi görünüyor:

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

1
Bu çok daha hızlı görünüyorinspect.stack
kentwait

Yine de, genellikle tavsiye edilmeyen korumalı bir üye kullanır, çünkü sysmodül biraz ağır yeniden düzenleme yaptıktan sonra başarısız olabilir .
filiprem

29

Modül ve sınıf dahil olmak üzere tam bir yöntem adı oluşturmaya çalışan biraz daha uzun bir sürümle geldim.

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)

Harika, bu benim giriş kodunda benim için iyi çalıştı, burada birçok farklı yerden çağrılabilir. Çok teşekkürler.
little_birdie

1
Siz de silmediğiniz sürece stack, teftiş belgelerinde
ankostis

@ankostis Bunu kanıtlamak için test kodunuz var mı?
anatoly techtonik

1
Bir yorumda gösterilmesi zor ... Bu sürüş kodunu (bellekten yazarak) bir düzenleyiciye kopyalayıp yapıştırın ve kodunuzun her iki sürümünü de deneyin: `` `zayıfref sınıfını içe aktar C: pass def kill (): print ('Killed' ) def leaking (): caller_name () local_var = C () poorref.finalize (local_var, kill) sızdırıyor () print ("Local_var öldürülmüş olmalı") `` Şunları elde etmelisiniz: `` `` Killed Local_var öldürüldü `` ve değil: `` Local_var öldürülmüş olmalı ``
ankostis

1
Müthiş! Diğer çözümler başarısız olduğunda işe yaradı! Sınıf yöntemleri ve lambda-ifadeleri kullanıyorum, bu yüzden zor.
osa

17

Kullanırdım inspect.currentframe().f_back.f_code.co_name. Kullanımı, çoğunlukla üç türden biri olan önceki cevapların hiçbirinde ele alınmamıştır:

  • Önceki bazı cevaplar kullanılır, inspect.stackancak çok yavaş olduğu bilinmektedir .
  • Önceden sys._getframealt çizgisi göz önüne alındığında, dahili özel işlev olan bazı önceki yanıtlar kullanılır ve bu nedenle kullanımı dolaylı olarak önerilmez.
  • Önceki bir cevap kullanır, inspect.getouterframes(inspect.currentframe(), 2)[1][3]ancak neye [1][3]eriştiği tamamen belirsizdir .
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()

Unutmayın ki cast(FrameType, frame)tatmin etmek için kullanılır mypy.


Teşekkür: 1313e tarafından bir cevap için ön yorum .


10

Yukarıdaki şeylerin bir birleşimi. Ama işte benim çatlamam.

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

kullanın:

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

def bar():
    return foo()

def baz():
    return bar()

def fizz():
    return baz()

fizz()

çıktı

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

2
Bu, istenen yığın derinliği gerçek değerden büyükse bir IndexError hatası yükseltir. modules = [(index, inspect.getmodule(stack[index][0])) for index in reversed(range(1, min(stack_size, len(inspect.stack()))))]Modülleri almak için kullanın .
jake77

1

Sınıflar arasında gidiyorsanız ve yöntemin ait olduğu VE yöntemine sınıf istiyorsanız bir yol buldum. Bir parça çıkarma işi gerektirir, ancak bunu ifade eder. Bu Python 2.7.13'te çalışır.

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$
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.