从该函数内部确定函数名称

2025-01-13 08:53:00
admin
原创
102
摘要:问题描述:有没有办法从函数内部确定函数的名称?def foo(): print("my name is", __myname__) # <== how do I calculate this at runtime? 在上面的例子中, 的主体foo将以某种方式访问​​函数名称...

问题描述:

有没有办法从函数内部确定函数的名称?

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

在上面的例子中, 的主体foo将以某种方式访问​​函数名称,"foo"而无需对其进行硬编码。输出将是:

>>> foo()
my name is foo

解决方案 1:

import inspect

def foo():
   print(inspect.stack()[0][3])
   print(inspect.stack()[1][3])  # will give the caller of foos name, if something called foo

foo()

输出:

foo
<module_caller_of_foo>

解决方案 2:

如果您不想自己操作堆栈,那么您应该根据上下文使用"bar"或。bar.__name__

Python 没有在函数本身内访问函数或其名称的功能。有人__function__曾为 Python 3.0 提出过一个魔法,但被拒绝了。请参阅PEP 3130 – 访问当前模块/类/函数

给出的拒绝通知是:

此 PEP 被拒绝。它不清楚应该如何实现,也不清楚在边缘情况下应该有什么样的精确语义,也没有给出足够多的重要用例。回应最多只是冷淡。

解决方案 3:

有几种方法可以得到相同的结果:

import sys
import inspect

def what_is_my_name():
    print(inspect.stack()[0][0].f_code.co_name)
    print(inspect.stack()[0][3])
    print(inspect.currentframe().f_code.co_name)
    print(sys._getframe().f_code.co_name)

请注意,inspect.stack这些调用比其他方法慢数千倍:

$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop

更新于 2021 年 8 月(原帖为 Python2.7 撰写)

Python 3.9.1 (default, Dec 11 2020, 14:32:07)
[GCC 7.3.0] :: Anaconda, Inc. on linux

python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
500 loops, best of 5: 390 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
500 loops, best of 5: 398 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
2000000 loops, best of 5: 176 nsec per loop
python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
5000000 loops, best of 5: 62.8 nsec per loop

解决方案 4:

functionNameAsString = sys._getframe().f_code.co_name

我想要一个非常类似的东西,因为我想将函数名称放在日志字符串中,该字符串出现在我的代码中的许多地方。这可能不是最好的方法,但这里有一种方法可以获取当前函数的名称。

解决方案 5:

您可以使用@Andreas Jung 展示的方法获取定义该函数的名称,但这可能不是调用该函数的名称:

import inspect

def Foo():
   print inspect.stack()[0][3]

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

我无法说这种区别对你来说是否重要。

解决方案 6:

这实际上是从该问题的其他答案中得出的。

以下是我的看法:

import sys

# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


def testFunction():
    print "You are in function:", currentFuncName()
    print "This function's caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

与使用 inspect.stack() 相比,此版本的优势可能在于它应该快数千倍[参见 Alex Melihoff 的帖子和关于使用 sys._getframe() 与使用 inspect.stack() 的时间对比]。

解决方案 7:

我把这个方便的工具放在附近:

import inspect
myself = lambda: inspect.stack()[1][3]

用法:

myself()

解决方案 8:

我想inspect这是最好的办法。例如:

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])

解决方案 9:

我找到了一个可以写入函数名称的包装器

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()

这将打印

我的时髦名字

存根

解决方案 10:

使用__name__属性:

# foo.py
def bar():
    print(f"my name is {bar.__name__}")

您可以使用属性从函数内部轻松访问函数的名称__name__

>>> def bar():
...     print(f"my name is {bar.__name__}")
...
>>> bar()
my name is bar

我自己也多次遇到过这个问题,正在寻找解决方法。正确答案包含在 Python 的文档中(请参阅可调用类型部分)。

每个函数都有一个__name__返回其名称的参数,甚至还有__qualname__一个返回其全名的参数,包括它所属的类(参见限定名称)。

解决方案 11:

print(inspect.stack()[0].function)似乎也有效(Python 3.5)。

解决方案 12:

我不知道为什么人们会把它复杂化:

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))

解决方案 13:

这是一种面向未来的方法。

将@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 上测试

解决方案 14:

import inspect

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

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

def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
    bar()

def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())

foo()
bar()

在 IDE 中代码输出

你好,我是 foo,爸爸是

你好,我是酒吧,爸爸是foo

你好,我是酒吧,爸爸是

解决方案 15:

import sys

def func_name():
    """
    :return: name of caller
    """
    return sys._getframe(1).f_code.co_name

class A(object):
    def __init__(self):
        pass
    def test_class_func_name(self):
        print(func_name())

def test_func_name():
    print(func_name())

测试:

a = A()
a.test_class_func_name()
test_func_name()

输出:

test_class_func_name
test_func_name

解决方案 16:

使用装饰器可以很容易地实现这一点。

>>> from functools import wraps

>>> def named(func):
...     @wraps(func)
...     def _(*args, **kwargs):
...         return func(func.__name__, *args, **kwargs)
...     return _
... 

>>> @named
... def my_func(name, something_else):
...     return name, something_else
... 

>>> my_func('hello, world')
('my_func', 'hello, world')

解决方案 17:

您可以使用装饰器:

def my_function(name=None):
    return name

def get_function_name(function):
    return function(name=function.__name__)

>>> get_function_name(my_function)
'my_function'

解决方案 18:

从上面所有使用该库的答案来看inspect,所有人都在写类似这样的内容:

import inspect

inspect.stack()[0][3]

但是,由于返回的inspect.stack()[0]是以下形式的 NamedTuple:

FrameInfo(frame=<frame at 0x103578810, file '<stdin>', line 1, code <module>>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)

只需按名称即可调用,即inspect.stack()[0].function

这里可以看到一个小的虚拟示例:

    def test_train_UGRIZY_noZ(self, architecture, dataset, hyperrun, wloss):
        log.warning(f"{inspect.stack()[0].function} -- Not Implemented Yet")
        pass

运行时打印:

WARNING - test_train_UGRIZY_noZ -- Not Implemented Yet

解决方案 19:

我用自己的方法在多重继承场景中安全地调用超级(我把所有的代码都放出来了)

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
'
        safe_super(A, self)()

class B(object):

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

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

class C(A, B):

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

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

测试它:

a = C()
a.test()

输出:

called from C
called from A
called from B

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

解决方案 20:

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

我向你提出这样的建议:

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')

解决方案 21:

我最近尝试使用上述答案从该函数的上下文访问该函数的文档字符串,但由于上述问题仅返回名称字符串,因此它不起作用。

幸运的是,我找到了一个简单的解决方案。如果您像我一样想要引用该函数,而不是简单地获取表示名称的字符串,则可以将 eval() 应用于函数名称的字符串。

import sys
def foo():
    """foo docstring"""
    print(globals()[sys._getframe().f_code.co_name].__doc__)

解决方案 22:

由于在python 3.9sys._getframe().f_back.f_code.co_name中根本不起作用,因此从现在开始可以使用以下命令:

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(...)

解决方案 23:

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

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由于该函数是全局变量,因此我们需要在调用时保存和恢复。f()上面的调用会产生:

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

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

>>> 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

解决方案 24:

@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) 

来自包装函数:执行名为:thanks 的函数,参数为:('Thank you', 'two'),关键字参数为:{'username': '@jeff-laughlin'}。来自包装函数:<function thanks
at

0x7f13e6ceaa60>

来自内部函数:感谢 @jeff-laughlin 教我闭包和装饰器的两个概念!

来自包装函数:执行 thanks 耗时 2.193450927734375e-05 秒。--------------------------------------------------------------------------------
来自包装函数:执行名为:thanks 的函数,参数为:('Thank you', 'two'),关键字参数为:{'username': 'stackoverflow'}。来自
包装函数:<function thanks at 0x7f13e6ceaa60>
来自内部函数:感谢 stackoverflow 教我闭包和装饰器的两个概念!
来自包装函数:执行感谢耗时 7.152557373046875e-06 秒。
<function thanks at 0x7f13e6ceaca0>

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

解决方案 25:

str(str(inspect.currentframe())).split(' ')[-1][:-1]

解决方案 26:

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()

解决方案 27:

实现你自己的装饰器

foo.py

from mydecorators import *

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

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
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用