【问题标题】:Creating a Logging Class Wrapper创建日志类包装器
【发布时间】:2021-10-24 01:48:07
【问题描述】:

我正在尝试编写一个快速装饰器来管理各种函数的日志记录返回。我不是非常精通装饰器,所以您可以提供的任何帮助都会非常有帮助!

from functools import update_wrapper
from typing import Any, Optional
from logging import getLogger
from time import perf_counter
from datetime import datetime

class logger:

    def __init__(self, func:callable, response:str = "debug"):
        self.logger = getLogger()
        self.func = func
        self.response = response

        update_wrapper(self, func)

    def __call__(self, *args, **kwargs):
        return getattr(self, self.response)

    def debug(self, *args, **kwargs):
        self.logger.debug(f"Running {__name__} with id: {id(self)} at {datetime.now()}")
        start = perf_counter()
        value = self.func(*args, **kwargs)
        end = perf_counter()
        self.logger.debug(f"""Completed {__name__} with id: {id(self)} at {datetime.now()}.
            Total Time to run: {end - start:.6f}s""")
        return value

    def info(self, *args, **kwargs):
        self.logger.info(f"Running {__name__} at {datetime.now()}.")
        return self.func(*args, **kwargs)

@logger(response="debug")
def stuff(x):
    return x*x

stuff(2)

我收到的错误是:

TypeError: __init__() missing 1 required positional argument: 'func',

显然,它不喜欢所需的可调用对象和响应要求。但是,我在所有其他基于类的装饰器设置中看到 func 需要作为 __init__ 的一部分调用,而且我还看到您可以传递装饰器添加信息。我在这里做错了什么?

编辑:

getattr(self, self.response) 的目的是使__call__ 返回的函数是与debug 或info 日志记录一起使用的函数。这允许我使用装饰器 @logging 进行日志记录和调试,但会根据装饰器中指定的响应值(即 @logging(response="info"))产生两种不同的结果。

解决方案:

class logger:

    def __init__(self, response:str = "debug"):
        self.logger = getLogger()
        self.response = response

    def __call__(self, func:callable):
        update_wrapper(self, func)
        self.func = func
        return getattr(self, self.response)

    def debug(self, *args, **kwargs):
        self.logger.debug(f"Running {self.func.__name__} (type:{type(self.func)}) with id: {id(self)} at {datetime.now()}")
        start = perf_counter()
        value = self.func(*args, **kwargs)
        end = perf_counter()
        self.logger.debug(f"""Completed {self.func.__name__} with id: {id(self)} at {datetime.now()}.
            Total Time to run: {end - start:.6f}s""")
        return value

    def info(self, *args, **kwargs):
        self.logger.info(f"Running {self.func.__name__} at {datetime.now()}.")
        return self.func(*args, **kwargs)

【问题讨论】:

  • func 应该是 call.... 的参数,并且 call 的参数应该以某种方式重新调整。可以尝试制作所需语法的示例吗?
  • 查看发生了什么撤消 @at stuff 函数并在其声明后运行 logger(stuff)(7)。错误消失。然后你必须正确地重新实现装饰器
  • @cards,我想这是让我感到困惑的部分。 logger(stuff)(7) 显然有效。包装部分 @ 创建它的 logger(stuff) 方面吗?然后,当您拨打stuff(7) 时,您实际上是在拨打logger(stuff)(7)。
  • 您必须跟踪类中方法的“调用”顺序:先是__init__,然后是__call__。像@my_decorator 这样的装饰器方面作为要调用的函数的下一个参数。如果装饰器接受参数 @my:decorator2( some_args) 那么下一个应该再次是一个函数。注意顺序以及是否需要参数
  • 最后的调用将执行装饰!所以它应该只包含func作为参数(最终**kwargs...你可以测试它)

标签: python class decorator python-decorators python-3.8


【解决方案1】:

我不知道你的代码应该做什么,特别是不清楚(对我来说)应该将哪种参数传递给getattr(self, self.response)(*args, **kwargs)。我这样说是为了了解装饰器的正确工作流程。

所以你的代码永远不会工作。这里有一些可能的装饰示例:

__call__方式:@logger(response="debug")

class logger_1:

    def __init__(self, response:str = "debug"):
       print(response)

    def __call__(self, func):
        self.func = func
        return self # ? depends on what are you doing

    def debug(self, *args, **kwargs):
        # ...

    def info(self, *args, **kwargs):
        #... 

@logger_1(response="debug")
def stuff(x):
    return x*x

更“抽象”的层次:@logger(response="debug").('some_parameter').debug_method

class logger_2:

    def __init__(self, response:str = "debug"):
       print(response)

    def __call__(self, *args, **kwargs):
       self.updated_response = getattr(self, self.response)(*args, **kwargs) # just an example
       return self

    def debug_method(self, func):
       self.func = func
       # ...
       return func

    def debug(self, *args, **kwargs):
        # ...

    def info(self, *args, **kwargs):
        #... 

@logger_2(response="debug")('some_parameter').debug_method
def stuff(x):
    return x*x

注意: logger_2(response="debug").('some_parameter').debug_method 没有接受参数,因为它等待与目标函数 stuff 一起“馈送”

这些是限制工作流程的语法示例,因此在设计装饰器时需要小心

【讨论】:

  • 看起来我遇到了两个主要问题。首先,我在 __init__ 而不是 __call__ 中调用函数。从根本上说,这对我来说是有意义的,但是,我看到的每个类装饰器示例都在 __init__ 中初始化了函数。所以我很困惑大多数例子是如何工作的。其次,我是通过担心参数而不是新函数来返回一个值。
  • @WolVes 谨慎编辑!如果你用__init__ 装饰,那么它的参数必须是函数......这取决于我们想要多少层“初始”:)
  • 查看我发布的解决方案,看看这对您是否有意义。我想确保你得到解决方案的功劳。
  • 这是响应和 getattr() 的目的。您将使用 getattr(self, "debug") 获得函数调试。由于 getattr 返回分配给名为“debug”的自我的属性。
  • 很高兴听到这个消息。如果您我应该修改我的答案或添加 smt,请给我留言,我明天会这样做。玩得开心!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 2014-04-13
相关资源
最近更新 更多