【问题标题】:Chain of decorators in Python 2.7Python 2.7 中的装饰器链
【发布时间】:2014-09-24 16:02:39
【问题描述】:

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

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:这不是我要解决的真正问题,但与我要使用的模式非常相似,也就是说,我需要通过多个过滤器来管道我的数据流以获得我想要的东西。我知道创建一个功能链(类似于责任链模式)来解决这个问题。想确定这是否可以通过装饰器实现。

【问题讨论】:

  • 那么当您执行even_number_filter(number_filter(sum_fn))(1,2,'',10, {}, None) 时会发生什么?不是你想要的?对于这种模式,我个人建议使用函数式编程(例如 toolz 包)。

标签: python python-decorators


【解决方案1】:

您应该修改您的函数以正确处理*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

【讨论】:

    猜你喜欢
    • 2012-10-17
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 1970-01-01
    • 2017-03-18
    相关资源
    最近更新 更多