【问题标题】:Passing optional keyword arguments to a decorated function将可选关键字参数传递给修饰函数
【发布时间】:2021-09-11 01:03:53
【问题描述】:

我想创建一个装饰器,将可选关键字传递给它包装的函数。像这样的:

@mydecorator
def dummy_without_magic_word():
  print('hello world')

@mydecorator
def dummy_with_magic_word(magic_word):
  print(magic_word)

基本上,如果被包装的函数有特定的关键字,应该由装饰器提供;否则,只需运行包装的函数。

实现此目的的正确方法是什么?目前我只是尝试传入关键字并检查 TypeError:

try:                                                                                           
  output = fn(redirect=redirect, **kwargs)                                               
except TypeError as e:
  if 'unexpected keyword argument' not in str(e):                                              
    raise e                                                                                    
  output = fn(*args, **kwargs)                                                           

但这看起来很脆弱/不直观/奇怪。

【问题讨论】:

  • 函数装饰在函数定义后立即发生,因此您需要将参数传递给装饰器,而不是调用时的函数。

标签: python decorator


【解决方案1】:

sintaxe 是这样的:

def decorator(*args, **kwargs):
    print("Inside decorator")
     
    def inner(func):
         
        print("Inside inner function")
        print("I like", kwargs['param'])
         
        func()
         
    # reurning inner function   
    return inner
 
@decorator(param = "Hello world")
def my_func():
    print("Inside actual function")

或者:

@mydecorator
def dummy_with_magic_word(magic_word = 'hello world'):
  print(magic_word)

【讨论】:

    【解决方案2】:

    要检查参数名称,您可以像这样深入研究函数对象:

    arguments = fn.__code__.co_varnames[:fn.__code__.co_argcount]
    if "redirect" in arguments:
        ...
    

    【讨论】:

      【解决方案3】:

      要实现这一点,您可以使用inspect 模块。使用getfullargspec 函数,您可以访问函数中定义的所有参数名称。需要注意的是,如果您想参数化传递给装饰函数的默认值,您将需要一个装饰器工厂。这是一个代码示例:

      import inspect    
      
      def my_decorator(default_value): # define decorator factory
          def decorator(fn): # define decorator
              def inner(*args, **kwargs): 
                  fn_args = inspect.getfullargspec(fn).args # recover decorated function
                                                            # arguments
                  
                  if "magic_word" in fn_args: # check for argument
                      return fn(*args, magic_word=default_value, **kwargs)
                  else:
                      return fn(*args, **kwargs)
              return inner
          return decorator
      
      @my_decorator(default_value='Goodbye World')
      def function_1():
          print('Hello World')
      
      @my_decorator(default_value='Goodbye World')
      def function_2(magic_word):
          print(magic_word)
      

      使用此装饰器,以下代码的结果将是:

      >>> function_1()
      'Hello World'
      >>> function_2()
      'Goodbye World'
      

      请注意,如果您只有关键字参数,则必须稍微修改装饰器:

      def my_decorator(default_value): # define decorator factory
          def decorator(fn): # define decorator
              def inner(*args, **kwargs): 
                  fn_args = inspect.getfullargspec(fn).args # recover decorated function
                                                            # arguments
                  fn_args += (inspect.getfullargspec(fn).kwonlyargs) # add kwonly args
      
                  if "magic_word" in fn_args: # check for argument
                      return fn(*args, magic_word=default_value, **kwargs)
                  else:
                      return fn(*args, **kwargs)
              return inner
          return decorator
      
      @my_decorator(default_value='Goodbye World')
      def function_3(a, *, magic_word):
          print(a, magic_word)
      
      >>> function_3(1)
      1 Goodbye World
      

      请注意,此实现有一个重要限制,即您必须对要查找的参数名称进行硬编码。情况就是这样,因为 python 不能评估参数名称的表达式。例如,如果你尝试这个实现:

      def my_decorator(search_word, default_value): # define decorator factory
          def decorator(fn): # define decorator
              def inner(*args, **kwargs): 
                  fn_args = inspect.getfullargspec(fn).args # recover decorated function
                                                            # arguments
                  fn_args += (inspect.getfullargspec(fn).kwonlyargs) # add kwonly args
      
                  if search_word in fn_args: # check for argument
                      return fn(*args, search_word=default_value, **kwargs)
                  else:
                      return fn(*args, **kwargs)
              return inner
          return decorator
      
      @my_decorator(search_word='magic_word', default_value='Goodbye World')
      def function_4(magic_word):
          print(magic_word)
      

      运行代码会报错:

      >>> function_4()
      Traceback (most recent call last):
        File "/test.py", in <module>
          function_4()
        File "/test6.py", in inner
          return fn(*args, search_word=default_value, **kwargs)
      TypeError: function_4() got an unexpected keyword argument 'search_word'
      

      【讨论】:

      • @theahura,这能回答你的问题吗?
      猜你喜欢
      • 1970-01-01
      • 2010-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-01
      • 2021-11-21
      • 2013-06-29
      相关资源
      最近更新 更多