【问题标题】:Is it possible to pass arguments to a decorator while calling a function in Python?在 Python 中调用函数时是否可以将参数传递给装饰器?
【发布时间】:2022-06-20 05:46:11
【问题描述】:

有谁知道在 Python 中调用函数时是否可以将参数传递给装饰器?

到目前为止,我只在函数定义中看到:

@decorator("This is a decorator", 66)
def func(a: int, b: int) -> None:
    pass

但我想知道是否可以在调用函数时这样做。

对于那些想知道我为什么要这样做的人,这是因为我正在处理其他人的工作,他在他的代码中使用了很多装饰器。但直到现在他只需要在函数定义时传递参数,因为函数只被调用一次。装饰器用于打印有关使用该函数的上下文的信息日志,但在我的情况下,由于该函数可以在不同的位置调用,因此上下文在函数调用位置的功能上可能不同。

【问题讨论】:

  • 您的推理不清楚,请提供minimal reproducible example。简短的回答是“不”,因为装饰器是在定义时应用的
  • 装饰器在函数被调用时并不“存在”。装饰器通过返回一个新函数来替换/增加函数定义。我认为你需要让装饰器使用一个全局状态,这个状态可能会受到调用函数或类似的东西的影响。不过,我认为这通常不是一个好主意。

标签: python function decorator


【解决方案1】:

简短的回答是,但你可以修改装饰器函数的代码;例如:

def decorator(func):
    def inner(*args,**kwargs):
        return func(*args,**kwargs)
    return inner

这是一个标准的装饰器函数,我们稍微修改一下。

你可以这样做:

def decorator(func,defaultdescription,defaultnum):
    def inner(description=defaultdescription,num=defaultnum,*args,**kwargs):
        print("function",func.__code__.co_name,"with description",description,"with number",num)
        return func(*args,**kwargs)

    return inner

所以你可以更改函数的描述

【讨论】:

    【解决方案2】:

    基本上,装饰器是另一个内部的函数,它在完成后调用您的函数。如果您打算使用自己的装饰器,则必须有一个内部函数来完成所需的任务并调用使用装饰器函数的函数。你可以使用 *args 和 **kwargs

    以下代码是python装饰器的基本实现

    # Python code to illustrate
    # Decorators basic in Python
    
    def decorator_fun(func):
    
    
    def inner(*args, **kwargs):
        """
        do your actual task here
        """
        for argv in args:
            print(argv)
    
        for key in kwargs:
            print(key)
        #returning to  function which used the decorator at last
        func()
        
    return inner
    
    @decorator_fun
    def func_to():
        print("Inside actual function")
    
    func_to("hello",{"test": "value 1"})
    

    【讨论】:

      【解决方案3】:

      正如其他人在此处所写,装饰器是函数的语法糖(即,使程序更易于阅读、编写或理解),它接收另一个函数作为参数并从内部激活它。

      所以,用装饰器调用这个“Add()”函数,像这样:

      @wrapper()
      def Add(x: int, y: int):
          return x + y
      

      这就像使用“Add”函数作为变量调用“wrapper”函数一样。像这样:

      wrapper(Add)(x,y) # pass x,y to wrapper that pass it to Add function.
      

      因此,(我发现)向装饰器添加参数的最佳方法是将其全部嵌套在另一个包含子装饰器的函数下。例如:

      @deco_maker(msg: str)
      def Add(x: int, y: int):
          return x + y
      

      会是这样的:

      deco_maker(msg)(wrapper(Add))(x,y)
      

      所以,这里有一个简单的包装装饰器,它记录函数调用,不带参数,如下所示:

      def wrapper(func: Callable):
          def wrapper_func(*args, **kwargs):
              logging.DEBUG f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
              value = func(*args, **kwargs)
              return value
          return wrapper_func
      

      这里是带有相关日志记录参数的扩展装饰器(日志名称和级别以获得更大的灵活性):

      def log_func_calls(logger_name: str, log_level: int):
          def wrapper(func: Callable):
              def wrapper_func(*args, **kwargs):
                  logger = logging.getLogger(logger_name)
                  logger.log(
                          level=log_level,
                          msg=f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
                          )
                  value = func(*args, **kwargs)
                  return value
              return wrapper_func
          return wrapper
      

      这是一个用于记录函数调用的参数化装饰器的完整代码示例,并在其后打印日志文件输出。

      示例

      import logging
      from typing import Callable
      
      # define app logger with file and console handlers
      def setup_logging():
          logger = logging.getLogger('test_app')
          logger.setLevel(logging.DEBUG)
          # create file handler which logs even debug messages
          fh = logging.FileHandler('test.log')
          fh.setLevel(logging.DEBUG)
          # create formatter and add it to the file handler
          formatter = logging.Formatter('{asctime} | {name} | {levelname:^8s} | {message}', style='{')
          fh.setFormatter(formatter)
          # add the handler to the logger
          logger.addHandler(fh)
          return logger
      
      # define a log decorator to trace function calls
      def log_func_calls(logger_name: str, log_level: int):
          def wrapper(func: Callable):
              def wrapper_func(*args, **kwargs):
                  logger = logging.getLogger(logger_name)
                  logger.log(
                          level=log_level,
                          msg=f"Function '{func.__name__}' called with args: {[str(arg) for arg in args]}."
                          )
                  value = func(*args, **kwargs)
                  return value
              return wrapper_func
          return wrapper
      
      # sample usage 1
      @log_func_calls(logger_name='test_app', log_level=logging.DEBUG)
      def Add(x: int, y: int):
          return x + y
      
      # sample usage 2
      @log_func_calls(logger_name='test_app', log_level=logging.DEBUG)
      def Sub(x: int, y: int):
          return x - y
      
      # a test run
      def main():
          logger = setup_logging()
          logger.info("<<< App started ! >>>")
          print(Add(50,7))
          print(Sub(10,7))
          print(Add(50,70))
          logger.info("<<< App Ended ! >>>")
      
      if __name__ == "__main__":
          main()
      

      还有日志输出:

      ...
      2022-06-19 23:34:52,656 | test_app |  DEBUG   | Function 'Add' called with args: ['50', '7'].
      2022-06-19 23:34:52,656 | test_app |  DEBUG   | Function 'Sub' called with args: ['10', '7'].
      2022-06-19 23:34:52,657 | test_app |  DEBUG   | Function 'Add' called with args: ['50', '70'].
      ...
      

      【讨论】:

        猜你喜欢
        • 2021-11-21
        • 1970-01-01
        • 1970-01-01
        • 2022-11-29
        • 2018-12-04
        • 2020-03-22
        • 2014-11-17
        • 2016-02-14
        相关资源
        最近更新 更多