【问题标题】:Determine whether *args is providing a specific named argument from a decorator确定 *args 是否提供来自装饰器的特定命名参数
【发布时间】:2019-12-13 17:38:04
【问题描述】:

我有一个装饰器,我想在那个装饰器中检查被装饰的函数是否有一个名为 query 的参数。如果它存在,我想像这样检测它在*args 中的位置:args[1]。我想在包装函数中执行此操作。我想做这样的事情:

def some_decorator(func):
    @wraps(func)
    def wrapper_func(*args, **kwargs):
        ind = 0
        query = ''
        for arg in args:
            if arg.__name__ == 'query'
                query = 'Found'
                break
            ind +=1
        if query == 'Found':
            query = args[ind]
    return wrapper_func

为了清楚起见,我这样使用它:

@some_decorator
def find_all(self, some_arg, query=None):
    pass

问题是,我也将装饰器用于其他功能,具有不同的签名,可能有也可能没有query 参数。我希望这能澄清一些事情。

【问题讨论】:

  • 我认为你做不到。 *args 仅包含值。没有办法知道名字。如果您需要知道名称,请使用**kwargs
  • @JohnGordon,...getting method parameter names in python 的可接受/有效答案的存在似乎与该声明相矛盾。
  • @CharlesDuffy 我认为这个问题提出了完全不同的问题。它问“如果我有一个函数的引用,我如何确定它的参数名称?”即如果我有一个函数def foo(arg1, arg2),我怎么能在运行时知道它的参数被命名为arg1arg2?但我对 this 问题的解释是,它需要 caller's 上下文中的 args 名称,即如果我调用 foo(x, y),foo 如何确定传递的参数是xy?不能。
  • 好吧,“不能”不做一些真正怪诞(而且可能是正确的)的事情,比如走到调用者的堆栈框架并检查其本地人以找到匹配的人,或者尝试分析它的AST 并希望它只对手头的函数进行一次调用。但我认为问题中没有任何依据,即 OP 正在寻找的是调用者上下文中的名称,而不是被调用函数上下文中的名称。
  • @EzizDurdyyev, ...所以,如果你想最终解决我与 JohnGordon 的争论,不仅要展示你是如何使用装饰器,而且你是如何调用修饰函数的。你会期望foo.find_all(baz, qux) 触发qux,因为它在查询位置被传递(正如我所料,在这种情况下 GCG 的答案是响应式的),或者因为调用者没有给它命名@而被忽略987654338@ 在它的上下文中(我理解约翰将你解释为所要求的)。

标签: python


【解决方案1】:

如果我对您的理解正确,那么我认为您可以使用inspect 模块:

import inspect
...
def some_decorator(func):
    def wrapper_func(*args, **kwargs):
        # get function signature using inspect.signature()
        signature = inspect.signature(func)
        # find the subset of arguments that are positional-only
        positional_args = [name for name in signature.parameters 
                           if signature.parameters[name].kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)]
        # This should give us the names of args that *may* be positional. 
        # They should also be in the proper order, since positional-or-keyword args
        # cannot precede positional-only args.
        # Now, we can search for a positional arg named 'query', get an index...
        try:
            query_idx = positional_args.index('query')
        except ValueError:
            query_idx = -1
        # ... and extract the corresponding value passed for that in *args
        if 0 <= query_idx < len(args):
            query = args[query_idx]
        else:
            query = None
        # then do something with that
        ...
    return wrapper_func

【讨论】:

  • AttributeError: 'list' object has no attribute 'find' for query_idx = positional_args.find('query')
  • 闻起来应该是index('query')而不是find('query')
  • @CharlesDuffy,它有帮助
  • @EzizDurdyyev 感谢您指出这一点,当我说的时候我在想str.find()。现已修复;我想知道为什么list 没有等效的方法?
【解决方案2】:

你可以试试array.index()

query = ''

try:
    idx = args.index('query')
except ValueError:
    idx = -1

if idx != -1:
   query = args[idx]

【讨论】:

  • 这是在寻找具有 value query 的参数,而不是具有 name query 的参数。请注意 OP 的伪代码如何引用参数的 .__name__,而不是其值。
猜你喜欢
  • 2014-04-26
  • 2018-01-12
  • 2022-01-08
  • 2021-09-14
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多