2010-04-16 18 views
113

Python: come ottenere il nome del metodo del chiamante nel metodo chiamato?Python: come ottenere il nome del metodo del chiamante nel metodo chiamato?

Si supponga che ho 2 metodi:

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

def method2(self): 
    ... 

Se io non voglio fare alcun cambiamento per method1, come ottenere il nome del chiamante (in questo esempio, il nome è Method1) in method2 ?

+2

Sì. Ora voglio solo generare documentazione e solo test. – zsong

+0

La tecnologia è una cosa, la metodologia è un'altra. – zsong

+3

possibile duplicato di [Ottenere il nome della funzione del chiamante all'interno di un'altra funzione in Python?] (Http://stackoverflow.com/questions/900392/getting-the-caller-function-name-inside-another-function-in-python) – ChrisF

risposta

152

inspect.getframeinfo e altre funzioni correlate a inspect possono aiutare:

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

questa introspezione è destinato per aiutare il debug e lo sviluppo; non è consigliabile fare affidamento su di esso per scopi di funzionalità di produzione. Versione

+11

"non è consigliabile fare affidamento su di esso per scopi di funzionalità di produzione." Perchè no? – beltsonata

+11

@beltsonata dipende dall'implementazione di CPython, quindi il codice che usa questo si interromperà se proverai a usare PyPy o Jython o altri runtime. va bene se stai solo sviluppando e eseguendo il debug localmente ma non realmente qualcosa che vuoi nel tuo sistema di produzione. – robru

+2

Ciao ragazzi! Dopotutto. Non è ancora pronta la produzione? –

69

Shorter:

import inspect 

def f1(): f2() 

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

f1() 

(con grazie a @alex, e Stefaan Lippen)

+0

Ciao, sto ottenendo sotto l'errore quando eseguo questo: File "/usr/lib/python2.7/inspect.py ", riga 528, in sourceource se non sourcefile e file [0] + file [-1]! = '<>': IndexError: indice di stringa non compreso nell'intervallo Può fornire un suggerimento. Grazie di anticipo – Pooja

+0

Questo KeyError: '__main__' – Praxiteles

22

mi è venuta in mente una versione leggermente più lunga che cerca di costruire un nome di metodo completo, compreso il modulo e la classe .

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) 
+0

Impressionante, questo ha funzionato bene per me nel mio codice di registrazione, dove posso essere chiamato da molti posti diversi. Grazie mille –

+0

A meno che non elimini anche 'stack', perde ancora fotogrammi a causa di riferimenti circolari come spiegato in [inspect-docs] (https://docs.python.org/3.6/library/inspect.html#the-interpreter-stack) – ankostis

+0

@ankostis hai qualche codice di test –

25

Questo sembra funzionare bene:

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

Bit di un amalgama di roba sopra. Ma ecco il mio crack.

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 

Usa:

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

def bar(): 
    return foo() 

def baz(): 
    return bar() 

def fizz(): 
    return baz() 

fizz() 

uscita è

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

Questo solleverà un IndexError se la profondità dello stack richiesta è maggiore di quella effettiva. Usa 'modules = [(index, inspect.getmodule (stack [index] [0])) per l'indice invertito (range (1, min (stack_size, len (inspect.stack()))))] per ottenere il moduli. – jake77

1

ho trovato un modo, se si sta andando tra classi e voglio la classe il metodo appartiene e il metodo. Ci vuole un po 'di lavoro di estrazione, ma fa il suo punto. Funziona in 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() 
Problemi correlati