从栈(Frame)对象中获取函数对象

ap0*_*ap0 5 python-3.x

logging我为我称为 的模块编写了一个自定义日志记录类call。对于这个类,我希望将其放置在任何函数/方法中,并记录函数名称及其参数以及调用函数时使用的所有值。

这对于类方法来说效果很好

Foo.bar(self, a=1, b=2, c=3, *args=(), **kwargs={'something': 4})
Run Code Online (Sandbox Code Playgroud)

使用这个最小的例子

import logging
import inspect

def call(logger):
    fname = []  # Function name including module and class
    fargs = []  # Arguments of function including positional and named arguments

    parentframe = inspect.stack()[1][0]
    module      = inspect.getmodule(parentframe)

    if module and module.__name__ != "__main__":
        fname.append(module.__name__)

    codename = parentframe.f_code.co_name

    if "self" in parentframe.f_locals:
        fname.append(parentframe.f_locals["self"].__class__.__name__)

    fobj = getattr(parentframe.f_locals["self"].__class__, codename)

    if codename != "<module>":
        fname.append(codename)

    argspec = inspect.formatargspec(*inspect.getfullargspec(fobj))
    args = argspec[1:-1].split(",")

    for arg in args:
        argkey = arg.strip().replace("*", "").split("=")[0]
        if arg == "self":
            fargs.append("self")
        else:
            fargs.append(arg.split("=")[0] + "=" + str(parentframe.f_locals[argkey]))

    del parentframe

    msg = ".".join(fname) + "(" + ",".join(fargs) + ")"
    if logger.isEnabledFor(30):
        logger.log(30, msg)


class Foo:

    def __init__(self, l):
        self.logger = l

    def bar(self, a, b, c=3, *args, **kwargs):
        call(self.logger)


if __name__ == "__main__":
    logging.addLevelName(30, "CALL")
    logger = logging.getLogger('blub')
    logger.level = 20
    f = Foo(logger)
    f.bar(1, 2, something=4)
    print("done...")
Run Code Online (Sandbox Code Playgroud)

我的问题是当我在静态方法或简单函数上使用相同的功能时。fobj = getattr(parentframe.f_locals["self"].__class__, codename) 它在我使用 获取函数对象 ()的行处失败self

parentframe 是Frame我认为问题中函数的对象。我还没有找到从该对象获取函数对象的方法。有办法吗?

M. *_*ber 4

用于getattr(module, codename)获取不包含在类中的函数的函数对象。

这里是完整的代码:

import logging
import inspect

def call(logger):
    fname = []  # Function name including module and class
    fargs = []  # Arguments of function including positional and named arguments

    parentframe = inspect.stack()[1][0]
    module      = inspect.getmodule(parentframe)

    if module and module.__name__ != "__main__":
        fname.append(module.__name__)

    codename = parentframe.f_code.co_name

    if "self" in parentframe.f_locals:
        fname.append(parentframe.f_locals["self"].__class__.__name__)
        fobj = getattr(parentframe.f_locals["self"].__class__, codename)
    else:
        fobj = getattr(module, codename)

    if codename != "<module>":
        fname.append(codename)

    argspec = inspect.formatargspec(*inspect.getfullargspec(fobj))
    args = argspec[1:-1].split(",")

    for arg in args:
        argkey = arg.strip().replace("*", "").split("=")[0]
        if arg == "self":
            fargs.append("self")
        else:
            fargs.append(arg.split("=")[0] + "=" + str(parentframe.f_locals[argkey]))

    del parentframe

    msg = ".".join(fname) + "(" + ",".join(fargs) + ")"
    if logger.isEnabledFor(30):
        logger.log(30, msg)


class Foo:

    def __init__(self, l):
        self.logger = l

    def bar(self, a, b, c=3, *args, **kwargs):
        call(self.logger)


def boo(a, b, c=3, *args, **kwargs):
    call(logger)


if __name__ == "__main__":
    logging.addLevelName(30, "CALL")
    logger = logging.getLogger('blub')
    logger.level = 20
    f = Foo(logger)
    f.bar(1, 2, something=4)
    boo(1, 2, something=4)
    print("done...")
Run Code Online (Sandbox Code Playgroud)