【问题标题】:How to get a function name as a string?如何将函数名作为字符串获取?
【发布时间】:2008-10-30 19:38:24
【问题描述】:

在 Python 中,如何在不调用函数的情况下将函数名作为字符串获取?

def my_function():
    pass

print get_function_name_as_string(my_function) # my_function is not in quotes

应该输出"my_function"

这样的函数在 Python 中可用吗?如果没有,关于如何在 Python 中实现 get_function_name_as_string 的任何想法?

【问题讨论】:

    标签: python string function


    【解决方案1】:
    my_function.__name__
    

    使用__name__ 是首选方法,因为它统一应用。与 func_name 不同,它也适用于内置函数:

    >>> import time
    >>> time.time.func_name
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    AttributeError: 'builtin_function_or_method' object has no attribute 'func_name'
    >>> time.time.__name__ 
    'time'
    

    双下划线也向读者表明这是一个特殊属性。作为奖励,类和模块也有一个__name__ 属性,所以你只需要记住一个特殊的名称。

    【讨论】:

    • 因为在某些情况下,您会获得一些函数对象作为函数的参数,并且您需要显示/存储/操作该函数的名称。也许您正在生成文档、帮助文本、操作历史记录等。所以不,您并不总是对函数名称进行硬编码。
    • @mgargiel:我的意思是:假设你有一个定义了 100 个方法的类,其中所有方法都只是包装器,它们调用私有方法,并传递包装器本身的名称。像这样的东西:def wrapper(kwargs): super(ExtendedClass,self).call('wrapper', kwargs)。您还有另一个选择,即:def wrapper(kwargs): super(ExtendedClass,self).call(sys._getframe().f_code.co_name, kwargs)。所以,我觉得 Albert Vonpupp 的答案更好。
    • @RichardGomes 一个答案适合在函数本身中获取名称,另一个适合从函数引用中获取名称。 OP的书面问题表明后者。
    • @RichardGomes 实际上我来这个问题是为了寻找解决这个问题的方法。我要解决的问题是创建一个可用于记录我所有调用的装饰器。
    • @RichardGomes 函数是一流的对象,可以绑定多个名称。不一定是f.__name__ == 'f'.
    【解决方案2】:

    要从其中获取当前函数或方法的名称,请考虑:

    import inspect
    
    this_function_name = inspect.currentframe().f_code.co_name
    

    sys._getframe 也可以代替 inspect.currentframe,尽管后者避免访问私有函数。

    要获取调用函数的名称,请考虑 f_backinspect.currentframe().f_back.f_code.co_name


    如果也使用mypy,它可以抱怨:

    错误:“Optional[FrameType]”的项目“None”没有属性“f_code”

    要抑制上述错误,请考虑:

    import inspect
    import types
    from typing import cast
    
    this_function_name = cast(types.FrameType, inspect.currentframe()).f_code.co_name
    

    【讨论】:

    • +1:这是我希望看到的答案。其他答案假设调用者已经知道函数名称,这在这个问题的上下文中是无稽之谈。
    • 理查德:不,它没有。您假设您在函数定义的同一范围内直接调用 name 或 func_name ,但通常情况并非如此。请记住,函数是对象 - 它们可以作为参数传递给其他函数,存储在列表/字典中以供以后查找或执行等。
    • @paulus_almighty,挖掘堆栈帧并没有让我觉得抽象!事实上,它有点与抽象相反。请参阅文档中的 implementation detail 注释。并非所有 Python 实现都包含sys._getframe——它直接连接到 CPython 的内部。
    • 这仅在函数内部有效,但问题指定不应调用该函数。
    • 如果你想把你的函数包装成更有用和更容易记住的东西,你必须像sys._getframe(1).f_code.co_name那样检索第1帧,所以你可以定义像get_func_name()这样的函数并期望得到所需的名称调用的函数。
    【解决方案3】:
    my_function.func_name
    

    函数还有其他有趣的属性。键入 dir(func_name) 以列出它们。 func_name.func_code.co_code 是编译后的函数,存储为字符串。

    import dis
    dis.dis(my_function)
    

    将以几乎人类可读的格式显示代码。 :)

    【讨论】:

    • f.__name__ 和 f.func_name 有什么区别?
    • Sam:名称是私有的,__names 是特殊的,存在概念上的差异。
    • 如果有人对 Matthew 前面的回答感到困惑,评论系统已将一些双下划线解释为粗体代码。用反引号转义,消息应为:__names__ 是私有的,__names 是特殊的。
    • 其实我觉得正确的是_names是私有的(前面一个下划线,只是一个约定),__names__是特殊的(前后双下划线)。不确定之前的双下划线是否有任何意义,无论是正式的还是作为惯例。
    • func_name 在 python3 中不再存在,所以如果你想要兼容性,你需要使用func.__name__
    【解决方案4】:

    如果您也对类方法感兴趣,Python 3.3+ 除了__name__ 之外还有__qualname__

    def my_function():
        pass
    
    class MyClass(object):
        def method(self):
            pass
    
    print(my_function.__name__)         # gives "my_function"
    print(MyClass.method.__name__)      # gives "method"
    
    print(my_function.__qualname__)     # gives "my_function"
    print(MyClass.method.__qualname__)  # gives "MyClass.method"
    

    【讨论】:

    • 如果方法有 @property 装饰器,这不起作用,有什么方法可以访问此实例中的名称?
    • @dusiod,看起来您可以从属性的 getter(使用 @property 装饰器创建)获取名称/qualname:MyClass.prop.fget.__qualname__ 给出 MyClass.prop
    【解决方案5】:

    这个函数会返回调用者的函数名。

    def func_name():
        import traceback
        return traceback.extract_stack(None, 2)[0][2]
    

    这就像 Albert Vonpupp 的回答,带有友好的包装。

    【讨论】:

    • 我在索引 [2] 处有“”,但以下内容有效:traceback.extract_stack(None, 2)[0][-1]
    • 对我来说这行不通,但可以:traceback.extract_stack()[-1][2]
    • 如果将第一个索引更改为 1,则此方法有效,你们应该在评论之前学会调试... traceback.extract_stack(None, 2)[1][2]
    【解决方案6】:
    import inspect
    
    def foo():
       print(inspect.stack()[0][3])
    

    在哪里

    • stack()[0]是调用者

    • stack()[3]是方法的字符串名称

    【讨论】:

    • 简单明了。 stack()[0] 将永远是调用者,[3] 是方法的字符串名称。
    【解决方案7】:

    我喜欢使用函数装饰器。 我添加了一个类,它也对函数时间进行计时。假设 gLog 是一个标准的 python 记录器:

    class EnterExitLog():
        def __init__(self, funcName):
            self.funcName = funcName
    
        def __enter__(self):
            gLog.debug('Started: %s' % self.funcName)
            self.init_time = datetime.datetime.now()
            return self
    
        def __exit__(self, type, value, tb):
            gLog.debug('Finished: %s in: %s seconds' % (self.funcName, datetime.datetime.now() - self.init_time))
    
    def func_timer_decorator(func):
        def func_wrapper(*args, **kwargs):
            with EnterExitLog(func.__name__):
                return func(*args, **kwargs)
    
        return func_wrapper
    

    所以现在你所要做的就是装饰它,瞧

    @func_timer_decorator
    def my_func():
    

    【讨论】:

      【解决方案8】:

      您只想获取函数的名称,这是一个简单的代码。 假设你定义了这些函数

      def function1():
          print "function1"
      
      def function2():
          print "function2"
      
      def function3():
          print "function3"
      print function1.__name__
      

      输出将是 function1

      现在假设您将这些函数放在一个列表中

      a = [function1 , function2 , funciton3]
      

      获取函数的名称

      for i in a:
          print i.__name__
      

      输出将是

      函数1
      函数2
      函数3

      【讨论】:

        【解决方案9】:

        作为@Demyn's answer 的扩展,我创建了一些实用函数来打印当前函数的名称和当前函数的参数:

        import inspect
        import logging
        import traceback
        
        def get_function_name():
            return traceback.extract_stack(None, 2)[0][2]
        
        def get_function_parameters_and_values():
            frame = inspect.currentframe().f_back
            args, _, _, values = inspect.getargvalues(frame)
            return ([(i, values[i]) for i in args])
        
        def my_func(a, b, c=None):
            logging.info('Running ' + get_function_name() + '(' + str(get_function_parameters_and_values()) +')')
            pass
        
        logger = logging.getLogger()
        handler = logging.StreamHandler()
        formatter = logging.Formatter(
            '%(asctime)s [%(levelname)s] -> %(message)s')
        handler.setFormatter(formatter)
        logger.addHandler(handler)
        logger.setLevel(logging.INFO)
        
        my_func(1, 3) # 2016-03-25 17:16:06,927 [INFO] -> Running my_func([('a', 1), ('b', 3), ('c', None)])
        

        【讨论】:

          【解决方案10】:

          sys._getframe() 不保证在 Python 的所有实现中都可用 (see ref),您可以使用 traceback 模块来做同样的事情,例如。

          import traceback
          def who_am_i():
             stack = traceback.extract_stack()
             filename, codeline, funcName, text = stack[-2]
          
             return funcName
          

          stack[-1] 的调用将返回当前进程的详细信息。

          【讨论】:

          • 对不起,如果sys._getframe() 未定义,那么traceback.extract_stack 也无法操作。后者提供了前者功能的粗略超集;你不能期望看到一个没有另一个。事实上,在 IronPython 2.7 中,extract_stack() 总是返回 []。 -1
          【解决方案11】:

          我见过一些使用装饰器的答案,尽管我觉得有些答案有点冗长。这是我用来记录函数名称以及它们各自的输入和输出值的东西。我在这里对其进行了调整,仅打印信息而不是创建日志文件,并将其调整为适用于 OP 特定示例。

          def debug(func=None):
              def wrapper(*args, **kwargs):
                  try:
                      function_name = func.__func__.__qualname__
                  except:
                      function_name = func.__qualname__
                  return func(*args, **kwargs, function_name=function_name)
              return wrapper
          
          @debug
          def my_function(**kwargs):
              print(kwargs)
          
          my_function()
          

          输出:

          {'function_name': 'my_function'}
          

          【讨论】:

          • 我喜欢这个而不是其他所有的,是只要添加 debug 函数一次,您可以通过简单地将装饰器添加到您需要或想要打印的任何功能来添加功能函数名。这似乎是最简单、适应性最强的。
          • 为什么需要try/except? __qualname__ 什么时候直接可用,什么时候不可用?
          【解决方案12】:
          import inspect
          
          def my_first_function():
              func_name = inspect.stack()[0][3]
              print(func_name)  # my_first_function
          

          或:

          import sys
          
          def my_second_function():
              func_name = sys._getframe().f_code.co_name
              print(func_name)  # my_second_function
          

          【讨论】:

            【解决方案13】:

            试试

            import sys
            fn_name = sys._getframe().f_code.co_name
            

            进一步参考 https://www.oreilly.com/library/view/python-cookbook/0596001673/ch14s08.html

            【讨论】:

              【解决方案14】:

              您可以使用特殊的__name__ 变量以字符串形式获取函数名称。

              def my_function():
                  pass
              
              print(my_function.__name__) # prints "my_function"
              

              【讨论】:

                猜你喜欢
                • 2010-12-06
                • 1970-01-01
                • 2011-11-17
                • 2018-01-07
                • 1970-01-01
                • 1970-01-01
                • 2018-12-03
                • 2023-01-21
                • 2022-11-19
                相关资源
                最近更新 更多