在Python中,如果不使用traceback模块,是否有一种方法可以从函数内部确定函数的名称?

假设我有一个模块foo和一个功能栏。当执行foo.bar()时,是否有一种方法让bar知道bar的名字?或者更好的是,foo。酒吧的名字吗?

#foo.py  
def bar():
    print "my name is", __myname__ # <== how do I calculate this at runtime?

当前回答

.f_back.f_code Sincesys._getframe()。Co_name在python 3.9中根本不起作用,下面可以从现在开始使用:

from inspect import currentframe


def testNameFunction() -> str:
    return currentframe().f_back.f_code.co_name


print(f'function name is {testNameFunction()}(...)')

结果:

function name is testNameFunction(...)

其他回答

import inspect


def method_name():
    return inspect.stack()[1][3]


def method_name_caller():
    return inspect.stack()[2][3]


def asdf():
    print(method_name_caller())
    print(method_name())


def asdf2():
    print(method_name_caller())
    print(method_name())
    asdf()

我用自己的方法在多重继承场景中安全地调用super(我把所有代码都放在这里)

def safe_super(_class, _inst):
    """safe super call"""
    try:
        return getattr(super(_class, _inst), _inst.__fname__)
    except:
        return (lambda *x,**kx: None)


def with_name(function):
    def wrap(self, *args, **kwargs):
        self.__fname__ = function.__name__
        return function(self, *args, **kwargs)
return wrap

示例用法:

class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A\n'
        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B\n'
        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C\n'
        safe_super(C, self)()

测试:

a = C()
a.test()

输出:

called from C
called from A
called from B

在每个@with_name修饰的方法中,您可以访问self。__fname__作为当前函数名。

我找到了一个包装器,它将写入函数名

from functools import wraps

def tmp_wrap(func):
    @wraps(func)
    def tmp(*args, **kwargs):
        print func.__name__
        return func(*args, **kwargs)
    return tmp

@tmp_wrap
def my_funky_name():
    print "STUB"

my_funky_name()

这将打印

my_funky_name 存根

我喜欢使用装饰器的想法,但我更喜欢避免触及函数参数。因此,我提供了另一种选择:

import functools

def withname(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        global __name
        __saved_name = globals().get("__name")
        __name = f.__name__
        ret = f(*args, **kwargs)
        __name = __saved_name
        return ret
    return wrapper

@withname
def f():
    print(f"in f: __name=={__name}")
    g()
    print(f"back in f: __name=={__name}")

@withname
def g():
    print(f"in g: __name=={__name}")

由于__name是一个全局变量,所以在调用函数时需要保存和恢复__name。调用上面的f()会产生:

in f: __name==f
in g: __name==g
back in f: __name==f

不幸的是,如果我们不改变函数参数,就没有全局变量的替代品。引用一个不是在函数上下文中创建的变量,将生成寻找全局变量的代码:

>>> def f(): print(__function__)
>>> from dis import dis
>>> dis(f)
  1           0 LOAD_GLOBAL              0 (print)
              2 LOAD_GLOBAL              1 (__function__)
              4 CALL_FUNCTION            1
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

我建议不要依赖于堆栈元素。如果有人在不同的上下文中使用你的代码(例如python解释器),你的堆栈将改变并破坏你的索引([0][3])。

我建议你这样做:

class MyClass:

    def __init__(self):
        self.function_name = None

    def _Handler(self, **kwargs):
        print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
        self.function_name = None

    def __getattr__(self, attr):
        self.function_name = attr
        return self._Handler


mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')