简体   繁体   English

将可选的关键字参数传递给装饰函数

[英]Passing optional keyword arguments to a decorated function

I would like to create a decorator that passes an optional keyword to the function it wraps.我想创建一个装饰器,将可选关键字传递给它包装的函数。 Something like this:像这样的东西:

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

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

Basically, if the wrapped function has a specific keyword, it should be provided by the decorator;基本上,如果被包装的函数有一个特定的关键字,它应该由装饰器提供; otherwise, just run the wrapped function.否则,只需运行包装的函数。

What is the right way to accomplish this?实现这一目标的正确方法是什么? Currently I'm just trying to pass in the keyword and check for a TypeError:目前我只是想传入关键字并检查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)                                                           

but this seems brittle/unintuitive/weird.但这似乎很脆弱/不直观/奇怪。

To achieve that you can use the inspect module.为此,您可以使用inspect模块。 Using the getfullargspec function, you can access all the argument names defined within the function.使用getfullargspec函数,您可以访问函数中定义的所有参数名称。 One remark is that if you want to parameterise the default value being passed to the decorated function, you will need a decorator factory.一个注释是,如果您想参数化传递给装饰函数的默认值,您将需要一个装饰器工厂。 Here is a code example:这是一个代码示例:

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)

Using this decorator, the result of the following code would be:使用此装饰器,以下代码的结果将是:

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

Notice that if you have keyword-only arguments, the decorator has to be slightly modified:请注意,如果您只有关键字参数,则必须稍微修改装饰器:

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

Note that this implementation has an important limitation which is the fact that you must hard code the argument name that you will look for.请注意,此实现有一个重要的限制,即您必须对要查找的参数名称进行硬编码。 This is the case, because python cannot cannot evaluate expressions for argument names.情况就是这样,因为 python 无法计算参数名称的表达式。 For example, if you try this implementation:例如,如果您尝试此实现:

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)

Running the code would raise an error:运行代码会引发错误:

>>> 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'

The sintaxe is like this:语法是这样的:

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")

Or:或者:

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

To inspect the argument names, you can dig into the function object like this:要检查参数名称,您可以像这样深入研究函数对象:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM