关于python:如何在被调用方法中获取调用方的方法名?

How to get the caller's method name in the called method?

Python:如何在被调用的方法中获取调用者的方法名?

假设我有两种方法:

1
2
3
4
5
6
def method1(self):
    ...
    a = A.method2()

def method2(self):
    ...

如果我不想对method1进行任何更改,如何在method2中获取调用者的名称(在此示例中,名称为method1)?


inspect.getframeinfo和inspect中的其他相关函数可以帮助:

1
2
3
4
5
6
7
8
9
10
>>> 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

这种内省旨在帮助调试和开发;不建议将其用于生产功能目的。


更短的版本:

1
2
3
4
5
6
7
8
import inspect

def f1(): f2()

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

f1()

(感谢@Alex和Stefaan Lippen)


这似乎工作得很好:

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


我想出了一个稍微长一点的版本,试图建立一个完整的方法名称,包括模块和类。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 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)


上面的东西融合了一点点。但这是我对它的抨击。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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('
'
.join(callers))

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

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@print_caller_name(4)
def foo():
    return 'foobar'

def bar():
    return foo()

def baz():
    return bar()

def fizz():
    return baz()

fizz()

输出是

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


我找到了一种方法,如果你跨越类并希望方法所属的类和方法。它需要一些提取工作,但它是重点。这适用于Python 2.7.13。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import inspect, os

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

class ClassTwo:
    def method2(self):
        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 4)
        print '
I 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()


你可以打印一个功能列表,如回答这里https://stackoverflow.com/a/56897183/4039061

1
2
import inspect;print(*['
\x1b[0;36;1m| \x1b[0;32;1m{:25}\x1b[0;36;1m| \x1b[0;35;1m{}'
.format(str(x.function), x.filename+'\x1b[0;31;1m:'+str(x.lineno)+'\x1b[0m') for x in inspect.stack()])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/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$