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

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

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

当前回答

@jeff-laughlin的回答很漂亮。我对它进行了轻微的修改,以达到我认为的目的:跟踪函数的执行,并捕获参数列表以及关键字参数。谢谢你@jeff-laughlin!

from functools import wraps                                                                                                                                                                                                     
import time                                                                                                                                                                                                                     
                                                                                                                                                                                                                                
def named(func):                                                                                                                                                                                                                
    @wraps(func)                                                                                                                                                                                                                
    def _(*args, **kwargs):                                                                                                                                                                                                     
        print(f"From wrapper function: Executing function named: {func.__name__}, with arguments: {args}, and keyword arguments: {kwargs}.")                                                                                    
        print(f"From wrapper function: {func}")                                                                                                                                                                                 
        start_time = time.time()                                                                                                                                                                                                
        return_value = func(*args, **kwargs)                                                                                                                                                                                    
        end_time = time.time()                                                                                                                                                                                                  
        elapsed_time = end_time - start_time                                                                                                                                                                                    
        print(f"From wrapper function: Execution of {func.__name__} took {elapsed_time} seconds.")                                                                                                                              
        return return_value                                                                                                                                                                                                     
    return _                                                                                                                                                                                                                    
                                                                                                                                                                                                                                
@named                                                                                                                                                                                                                          
def thanks(message, concepts, username='@jeff-laughlin'):                                                                                                                                                                       
    print(f"From inner function: {message} {username} for teaching me about the {concepts} concepts of closures and decorators!")                                                                                               
                                                                                                                                                                                                                                
thanks('Thank you', 'two', username='@jeff-laughlin')                                                                                                                                                                           
print('-'*80)                                                                                                                                                                                                                   
thanks('Thank you', 'two', username='stackoverflow')
print(thanks) 

From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': '@jeff-laughlin'}. From wrapper function: <function thanks at 0x7f13e6ceaa60> From inner function: Thank you @jeff-laughlin for teaching me about the two concepts of closures and decorators! From wrapper function: Execution of thanks took 2.193450927734375e-05 seconds. -------------------------------------------------------------------------------- From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': 'stackoverflow'}. From wrapper function: <function thanks at 0x7f13e6ceaa60> From inner function: Thank you stackoverflow for teaching me about the two concepts of closures and decorators! From wrapper function: Execution of thanks took 7.152557373046875e-06 seconds. <function thanks at 0x7f13e6ceaca0>

最让我惊讶的是,有一种方法可以在运行时拦截函数,检查它们,并基于此采取一些操作。另一件令人惊讶的事情是内部函数的内存地址两次都是相同的。有人知道这是为什么吗?在我能够理解这个装饰器/闭包魔法之前,我还有一段路要走。

其他回答

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

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

@jeff-laughlin的回答很漂亮。我对它进行了轻微的修改,以达到我认为的目的:跟踪函数的执行,并捕获参数列表以及关键字参数。谢谢你@jeff-laughlin!

from functools import wraps                                                                                                                                                                                                     
import time                                                                                                                                                                                                                     
                                                                                                                                                                                                                                
def named(func):                                                                                                                                                                                                                
    @wraps(func)                                                                                                                                                                                                                
    def _(*args, **kwargs):                                                                                                                                                                                                     
        print(f"From wrapper function: Executing function named: {func.__name__}, with arguments: {args}, and keyword arguments: {kwargs}.")                                                                                    
        print(f"From wrapper function: {func}")                                                                                                                                                                                 
        start_time = time.time()                                                                                                                                                                                                
        return_value = func(*args, **kwargs)                                                                                                                                                                                    
        end_time = time.time()                                                                                                                                                                                                  
        elapsed_time = end_time - start_time                                                                                                                                                                                    
        print(f"From wrapper function: Execution of {func.__name__} took {elapsed_time} seconds.")                                                                                                                              
        return return_value                                                                                                                                                                                                     
    return _                                                                                                                                                                                                                    
                                                                                                                                                                                                                                
@named                                                                                                                                                                                                                          
def thanks(message, concepts, username='@jeff-laughlin'):                                                                                                                                                                       
    print(f"From inner function: {message} {username} for teaching me about the {concepts} concepts of closures and decorators!")                                                                                               
                                                                                                                                                                                                                                
thanks('Thank you', 'two', username='@jeff-laughlin')                                                                                                                                                                           
print('-'*80)                                                                                                                                                                                                                   
thanks('Thank you', 'two', username='stackoverflow')
print(thanks) 

From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': '@jeff-laughlin'}. From wrapper function: <function thanks at 0x7f13e6ceaa60> From inner function: Thank you @jeff-laughlin for teaching me about the two concepts of closures and decorators! From wrapper function: Execution of thanks took 2.193450927734375e-05 seconds. -------------------------------------------------------------------------------- From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': 'stackoverflow'}. From wrapper function: <function thanks at 0x7f13e6ceaa60> From inner function: Thank you stackoverflow for teaching me about the two concepts of closures and decorators! From wrapper function: Execution of thanks took 7.152557373046875e-06 seconds. <function thanks at 0x7f13e6ceaca0>

最让我惊讶的是,有一种方法可以在运行时拦截函数,检查它们,并基于此采取一些操作。另一件令人惊讶的事情是内部函数的内存地址两次都是相同的。有人知道这是为什么吗?在我能够理解这个装饰器/闭包魔法之前,我还有一段路要走。

实现您自己的装饰器

# mydecorators.py

def resolve_function(func):
    #in case annotated func is an staticmethod
    if isinstance(func,staticmethod):
        return func.__func__
    return func

def print_my_name(func):
    def function_caller(*args,**kwargs):
        _func = resolve_function(func)
        print("my name is: %s" %_func.__name__)
        return _func(*args,**kwargs)
    return function_caller

然后使用它

# foo.py

from mydecorators import *

@print_my_name
def bar():
    #do something else
#in terminal: my name is: bar

我建议不要依赖于堆栈元素。如果有人在不同的上下文中使用你的代码(例如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')

这是一个经得起考验的方法。

将@CamHart和@Yuval的建议与@RoshOxymoron接受的答案结合起来,可以避免:

_hidden和潜在弃用的方法 在堆栈中建立索引(可以在未来的python中重新排序)

所以我认为这对未来的python版本(在2.7.3和3.3.2测试)很有帮助:

from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

更新:在3.7.10,3.8.10和3.9.5测试