【问题标题】:Parsing args and kwargs in decorators在装饰器中解析 args 和 kwargs
【发布时间】:2013-09-25 06:01:24
【问题描述】:

我有一个接受 args 和 kwargs 的函数,我需要根据函数中 2nd arg 的值在我的装饰器中做一些事情,如下面的代码所示:

def workaround_func():
    def decorator(fn):
        def case_decorator(*args, **kwargs):
            if args[1] == 2:
                print('The second argument is a 2!')
            return fn(*args, **kwargs)
        return case_decorator
    return decorator

@workaround_func()
def my_func(arg1, arg2, kwarg1=None):
    print('arg1: {} arg2: {}, kwargs: {}'.format(arg1, arg2, kwarg1))

问题是python允许用户使用第二个参数作为常规参数或关键字参数调用函数,因此如果用户使用arg2作为kwarg调用my_func,它会引发IndexError ,见下文:

In [8]: d.my_func(1, 2, kwarg1=3)
The second argument is a 2!
arg1: 1 arg2: 2, kwargs: 3

In [9]: d.my_func(1, arg2=2, kwarg1=3)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-9-87dc89222a9e> in <module>()
----> 1 d.my_func(1, arg2=2, kwarg1=3)

/home/camsparr/decoratorargs.py in case_decorator(*args, **kwargs)
      2     def decorator(fn):
      3         def case_decorator(*args, **kwargs):
----> 4             if args[1] == 2:
      5                 print('The second argument is a 2!')
      6             return fn(*args, **kwargs)

IndexError: tuple index out of range

有没有办法解决这个问题而不只是做一个try/except 并抓住IndexError

【问题讨论】:

    标签: python decorator


    【解决方案1】:

    我使用 python decorator 包找到了答案。这个包的一个特点是,无论用户如何传递它们,它都会保留位置/关键字参数。它具有减少大量代码的额外好处,所以我的原始代码:

    def workaround_func():
        def decorator(fn):
            def case_decorator(*args, **kwargs):
                if args[1] == 2:
                    print('The second argument is a 2!')
                return fn(*args, **kwargs)
            return case_decorator
        return decorator
    
    @workaround_func()
    def my_func(arg1, arg2, kwarg1=None):
        print('arg1: {} arg2: {}, kwargs: {}'.format(arg1, arg2, kwarg1))
    

    变成:

    from decorator import decorator
    
    @decorator
    def workaround_decorator(f, *args, **kwargs):
        if args[1] == 2:
            print('The second argument is 2!')
        return f(*args, **kwargs)
    
    @workaround_decorator
    def my_func(arg1, arg2, kwarg1=None):
        print('arg1: {} arg2: {}, kwargs: {}'.format(arg1, arg2, kwarg1))
    

    【讨论】:

      【解决方案2】:

      这是我能想到的最可靠的处理方式……诀窍是检查第二个参数的名称。然后,在装饰器中,检查kwargs 中是否存在该名称。如果是,那么您就使用它。如果不是,则使用args

      from inspect import getargspec    
      
      def decorate(fn):
          argspec = getargspec(fn)
          second_argname = argspec[0][1]
          def inner(*args, **kwargs):
              special_value = (kwargs[second_argname] 
                               if second_argname in kwargs else args[1])
              if special_value == 2:
                  print "foo"
              else:
                  print "no foo for you"
              return fn(*args, **kwargs)
          return inner
      
      @decorate
      def foo(a, b, c=3):
          pass
      
      foo(1,2,3)
      foo(1,b=2,c=4)
      foo(1,3,5)
      foo(1,b=6,c=5)
      

      运行此结果:

      foo
      foo
      no foo for you
      no foo for you
      

      正如预期的那样。

      【讨论】:

        猜你喜欢
        • 2018-08-28
        • 1970-01-01
        • 2016-04-20
        • 1970-01-01
        • 2014-04-26
        • 1970-01-01
        • 2021-07-26
        • 1970-01-01
        • 2013-02-01
        相关资源
        最近更新 更多