Python 2.7中的装饰链

时间:2014-09-24 16:02:40

标签: python python-decorators

我正在尝试使用装饰器来实现对象列表的过滤。我有一个对象列表,我只需要过滤掉整数,并进一步滤除偶数。最后我需要应用求和偶数的求和函数。以下是我的职能:

def number_filter(function):
    print 'i am number_filter and function is ' + function.__name__
    def wrapper3(*args, **kwargs):
        print 'wrapper3 args: ' + repr(args)
        l = []
        for a in args:
            try:
                l.append(int(a))
            except ValueError:
                pass
            except TypeError:
                pass                
        return function(l)
    return wrapper3

def even_number_filter(function):
    print 'i am even_number_filter and function is ' + function.__name__
    def wrapper1(*args, **kwargs):
        print 'wrapper1 args: ' + repr(args)
        l = [i for i in args if i % 2 == 0]
        return function(l)
    return wrapper1

def sum_fn(args):
    return sum(args)

以下来电独立完美:

>>> number_filter(sum_fn)(1,2,'',10, {}, None)
i am number_filter and function is sum_fn
wrapper3 args: (1,2,'',10, {}, None)
13
>>> even_number_filter(sum_fn)(1,2,10)
i am even_number_filter and function is sum_fn
wrapper1 args: (1, 2, 10)
12

我想要的是一种使用上面定义的两个装饰器最终获得偶数总和的方法。请注意,输入是列表(1,2,'',10, {}, None),预期输出为12

PS:这不是我试图解决的真正问题,但非常类似于我尝试使用的模式,也就是说,我需要通过几个过滤器来管理我的数据流以获得我想要的东西。我知道创建一系列功能(类似于责任链模式)来解决这个问题。想要确定是否可以通过装饰者来实现。

1 个答案:

答案 0 :(得分:2)

您应该修改您的函数以正确处理*args**kwargs(您当前不匹配单序列参数和*args打包/解包),但是一旦您完成了它,它就是容易嵌套装饰:

>>> def number_filter(function):
    print 'i am number_filter and function is ' + function.__name__
    def wrapper3(*args, **kwargs):
        print 'wrapper3 args: ' + repr(args)
        l = []
        for a in args:
            try:
                l.append(int(a))
            except ValueError:
                pass
            except TypeError:
                pass                
        return function(*l, **kwargs) # pass filtered args and unfiltered kwargs
    return wrapper3

>>> def even_number_filter(function):
    print 'i am even_number_filter and function is ' + function.__name__
    def wrapper1(*args, **kwargs):
        print 'wrapper1 args: ' + repr(args)
        l = [i for i in args if i % 2 == 0]
        return function(*l, **kwargs) # same again
    return wrapper1

>>> @number_filter # filter out non-numbers first
@even_number_filter # then odd numbers
def sum_fn(*args, **kwargs): # handle arbitrary arguments 
    return sum(args)

i am even_number_filter and function is sum_fn
i am number_filter and function is wrapper1
>>> sum_fn(1,2,'',10, {}, None) # note separate arguments, not a single sequence
wrapper3 args: (1, 2, '', 10, {}, None)
wrapper1 args: (1, 2, 10)
12 # success!

请注意,两个装饰器都做了类似的事情,所以你可以重构一个filter_args装饰器:

>>> import functools
>>> def filter_args(f):
    def decorates(fn):
        @functools.wraps(fn) # wrap decorators to handle docstrings
        def wrapper(*args, **kwargs):
            return fn(*filter(f, args), **kwargs)
        return wrapper
    return decorates

>>> @filter_args(lambda i: i % 2 == 0)
def sum_args(*args, **kwargs):
    """Sum the positional arguments."""
    return sum(args)

>>> sum_args(1, 2, 3, 4, 5)
6