2014-09-24 72 views
0

我想使用裝飾器來實現過濾對象列表。我有一個對象列表,我只需要過濾掉整數,並進一步過濾掉偶數。最後,我需要應用總和函數來總結偶數。以下是我的函數:Python中的裝飾器鏈2.7

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:這不是真正的問題,我正在嘗試解決,但非常類似於我想要使用的模式,也就是我需要的方式來管我的數據通過幾個過濾器流,以獲得我想要的。我知道創建一系列功能(類似於職責鏈模式)來解決這個問題。想要確定這是否可能通過裝飾器或不。

+0

當你'even_number_filter(number_filter(sum_fn))(1,2,「會發生什麼」,10 ,{},無)'?不是你想要的?我個人推薦這種模式的功能風格的編程(例如,toolz包)。 – mdurant 2014-09-24 16:13:22

回答

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