【问题标题】:How to pass a specific argument to a decorator in python如何将特定参数传递给python中的装饰器
【发布时间】:2010-06-30 17:57:18
【问题描述】:

我想编写一个 python 函数装饰器来测试函数的某些参数是否通过了某些标准。例如,假设我想测试一些参数总是偶数,那么我希望能够做这样的事情(不是有效的 python 代码)

def ensure_even( n )  :
  def decorator( function ) :
    @functools.wraps( function )
    def wrapper(*args, **kwargs):
      assert(n % 2 == 0)
      return function(*args, **kwargs)
    return wrapper
   return decorator


@ensure_even(arg2)
def foo(arg1, arg2, arg3) : pass

@ensure_even(arg3)
def bar(arg1, arg2, arg3) : pass

但我不知道如何实现上述目标。有没有办法将特定的参数传递给装饰器? (就像上面的 arg2 代表 foo 和 arg3 代表 bar)

谢谢!

【问题讨论】:

  • 制作参数装饰器的唯一方法是使用嵌套装饰器。

标签: python function decorator


【解决方案1】:

你可以这样做:

def ensure_even(argnum):
  def fdec(func):
    def f(*args, **kwargs):
      assert(args[argnum] % 2 == 0)  #or assert(not args[argnum] % 2)
      return func(*args, **kwargs)
    return f
  return fdec

那么:

@ensure_even(1)  #2nd argument must be even
def test(arg1, arg2):
  print(arg2)

test(1,2) #succeeds
test(1,3) #fails

【讨论】:

  • 是的,这就是我所做的,但我认为这只是我的 C++ 背景提出的一种技巧,而不是一种更优雅的 Python 方式:) 无论如何,谢谢。
  • @MK:如果您使用关键字参数而不是位置参数,那么您可以传递 ensure_even 关键字参数的名称。从某种意义上说,这可能会更好,因为硬编码的数字会少一个,并且您的代码可能会在添加新参数后幸存下来,而无需更改 argnum。
  • @~unutbu 是的,这是个好方法。
【解决方案2】:

我之前的回答错过了你问题的重点。

这里有一个更长的解决方案:

def ensure_even(*argvars):
  def fdec(func):
    def f(*args,**kwargs):
      for argvar in argvars:
        try:
          assert(not args[func.func_code.co_varnames.index(argvar)] % 2)
        except IndexError:
          assert(not kwargs[argvar] % 2)
      return func(*args,**kwargs)
    return f
  return fdec

所以这是用法:

@ensure_even('a','b')
def both_even(a,b):
    print a,b

@ensure_even('even')
def first_even(even, arg2):
    print even, arg2

both_even(2,2)
first_even(2,3)
both_even(2,1) #fails

虽然我不确定它是否适用于所有情况。

【讨论】:

  • 啊自省。我必须先去看看上面在做什么,但我明白了,这很酷。我将坚持上述解决方案
猜你喜欢
  • 2018-12-04
  • 1970-01-01
  • 2020-04-13
  • 2012-04-27
  • 2014-11-17
  • 2015-10-22
相关资源
最近更新 更多