Python how to get the calling function (not just its name)?

你离开我真会死。 提交于 2019-11-30 09:21:37

问题


I want to write a function which returns the calling function:

def foo():
    return get_calling_function() #should return 'foo' function object

There's numerous examples online how to get the calling function's name, but not how to get the actual object. I've come up with the following solution which gets the name, then looks it up in the calling function's global namespace. However this doesn't work for class functions since there you need the class name as well, and I image there's a bunch of other edge cases as well.

from inspect import stack
def get_calling_function():
    return stack()[2][0].f_globals[stack()[1][3]]

So any advice how or if its possible to write this function so that it works generically (on Python 3, btw)? Thanks.


回答1:


Calling can happen from any code object (and from an extension module/builtin): from exec, execfile, from module name space (during import), from within a class definition, from within a method / classmethod / staticmethod, from a decorated function/method, from within a nested function, ... - so there is no "calling function" in general, and the difficulty to do anything good with that.

The stack frames and their code objects are the most general you can get - and examine the attributes.


This one finds the calling function in many cases:

import sys, inspect

def get_calling_function():
    """finds the calling function in many decent cases."""
    fr = sys._getframe(1)   # inspect.stack()[1][0]
    co = fr.f_code
    for get in (
        lambda:fr.f_globals[co.co_name],
        lambda:getattr(fr.f_locals['self'], co.co_name),
        lambda:getattr(fr.f_locals['cls'], co.co_name),
        lambda:fr.f_back.f_locals[co.co_name], # nested
        lambda:fr.f_back.f_locals['func'],  # decorators
        lambda:fr.f_back.f_locals['meth'],
        lambda:fr.f_back.f_locals['f'],
        ):
        try:
            func = get()
        except (KeyError, AttributeError):
            pass
        else:
            if func.__code__ == co:
                return func
    raise AttributeError("func not found")

# Usage

def f():
    def nested_func():
        print get_calling_function()
    print get_calling_function()
    nested_func()

class Y:
    def meth(self, a, b=10, c=11):
        print get_calling_function()
        class Z:
            def methz(self):
                print get_calling_function()
        z = Z()
        z.methz()
        return z
    @classmethod
    def clsmeth(cls):
        print get_calling_function()
    @staticmethod
    def staticmeth():
        print get_calling_function()

f()
y = Y()
z = y.meth(7)
z.methz()
y.clsmeth()
##y.staticmeth()  # would fail

It finds:

<function f at 0x012E5670>
<function nested_func at 0x012E51F0>
<bound method Y.meth of <__main__.Y instance at 0x01E41580>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method Z.methz of <__main__.Z instance at 0x01E63EE0>>
<bound method classobj.clsmeth of <class __main__.Y at 0x01F3CF10>>


来源:https://stackoverflow.com/questions/39078467/python-how-to-get-the-calling-function-not-just-its-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!