【问题标题】:Get the parent class name of a function with a decorator使用装饰器获取函数的父类名
【发布时间】:2021-10-31 05:38:22
【问题描述】:

假设有一个类有函数:

class Tester:
    
    @Logger()
    def __init__(self):
        print(__class__)
    
    @Logger()
    def func(self, num):
        return num**2

其中Logger是一个装饰器,大致定义为:

from typing import Optional, Any
from logging import getLogger

class Logger:
    def __init__(self):
        self.logger = getLogger()
        self.logging_function = getattr(self, 'function')
        
    def __call__(self, decorator: callable):
        
        def f(*args, **kwargs):
            return self.logging_function(decorator, *args, **kwargs)
        
        return f
    
    def function(self, func: callable, *args: Optional[Any], **kwargs: Optional[Any]):
        func_name = Logger.get_name(func)
        self.logger.info(f"Starting: {func_name}.")
        return func(*args, **kwargs)
    
    @staticmethod
    def get_name(func):
        return f'__init__ {func.__class__.__name__}' if func.__name__ == '__init__' else func.__name__

我们如何编辑 Logger get_name 函数,这样如果正在运行的函数是 class __init__,则返回的名称是 __init__ Tester,但如果函数被命名为其他名称,它只会返回函数 @987654326 @?

(AKA)预期输出:

>>> test = Tester()
INFO: Starting __init__ Tester.
<class '__main__.Tester'>

>>> test.func(3)
INFO: Starting func. 
9

电流输出:

>>> test = Tester()
INFO: Starting __init__ function.
<class '__main__.Tester'>

>>> test.func(3)
INFO: Starting func. 
9

【问题讨论】:

  • 你的意思是用Logger()而不是Logger来装饰吗?
  • Logger 类定义了 Logger 装饰器。
  • 是的,我明白了,但是你是用类而不是它的实例来装饰的。这会导致错误。
  • 你说得对,我刚刚修好了。

标签: python python-3.x class decorator python-decorators


【解决方案1】:

您可以像这样使用qualified name

@staticmethod
def get_name(func):
    return func.__qualname__

这会给你类似的东西:

>>> test = Tester()
INFO:Starting: Tester.__init__.
<class '__main__.Tester'>
>>> test.func(3)
INFO:Starting: Tester.func.
9

您可能还对standard LogRecord attribute funcName 感兴趣,它在in 函数中执行类似的操作。一个基本的演示:

import logging
logging.basicConfig(
    level=logging.INFO,
    format="%(levelname)s (%(funcName)s) %(message)s",
)

log = logging.getLogger()

class A:
    def my_method(self):
        log.info("hello")

def bar():
    log.info("world")

A().my_method()
bar()

输出这个:

INFO (my_method) hello
INFO (bar) world

【讨论】:

    【解决方案2】:

    您可以使用以下函数来获取函数名。

    get_name 产生您的愿望输出的代码片段:

    @staticmethod
        def get_name(func):
            func_name = func.__qualname__.split('.') if '__init__' in func.__qualname__ else func.__name__
            func_name = ' '.join(func_name[::-1]) if isinstance(func_name, list) else func_name
            return func_name
    

    输出:

    Starting: __init__ Tester.
    <class '__main__.Tester'>
    Starting: func.
    

    如果你想要更通用,那么你可以使用以下函数。

    get_name 通用代码片段:

    @staticmethod
        def get_name(func):
            func_name = func.__qualname__.split('.') if '.' in func.__qualname__ else func.__name__
            func_name = ' '.join(func_name[::-1]) if isinstance(func_name, list) else func_name
            return func_name
    

    输出:

    Starting: __init__ Tester.
    <class '__main__.Tester'>
    Starting: func Tester.
    

    按照您的要求完成工作代码:

    from typing import Optional, Any
    from logging import getLogger
    
    
    class Logger:
        def __init__(self):
            self.logger = getLogger()
            self.logging_function = getattr(self, 'function')
            
        def __call__(self, decorator: callable):
            
            def f(*args, **kwargs):
                return self.logging_function(decorator, *args, **kwargs)
            
            return f
        
        def function(self, func: callable, *args: Optional[Any], **kwargs: Optional[Any]):
            func_name = Logger.get_name(func)
            self.logger.info(f"Starting: {func_name}.")
            return func(*args, **kwargs)
        
        @staticmethod
        def get_name(func):
            func_name = func.__qualname__.split('.') if '__init__' in func.__qualname__ else func.__name__
            func_name = ' '.join(func_name[::-1]) if isinstance(func_name, list) else func_name
            return func_name
    
    
    class Tester:
        
        @Logger()
        def __init__(self):
            print(__class__)
        
        @Logger()
        def func(self, num):
            return num**2
    
    test = Tester()
    test.func(3)
    

    输出:

    Starting: __init__ Tester.
    <class '__main__.Tester'>
    Starting: func.
    

    【讨论】:

      【解决方案3】:

      从 Python 3.3 开始,您应该可以使用 func.__qualname__ 到达那里。

      我个人会保持简单以保持一致性:

      @staticmethod
      def get_name(func):
          return func.__qualname__
      

      但要完全匹配您的预期输出,我相信这会起作用:

      @staticmethod
      def get_name(func):
          if '.__init__' in func.__qualname__:
              class_name = func.__qualname__.split('.')[-2]  # this prints the innermost type in case of nested classes
              return f'__init__ {class_name}'
          return func.__name__
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-12-31
        • 2020-12-18
        • 1970-01-01
        • 2019-08-30
        • 2011-06-20
        • 1970-01-01
        • 2019-07-23
        相关资源
        最近更新 更多