2011-06-13 53 views
5

我正在處理一個涉及從統一差異補丁驗證格式的問題。在python中調用next之前修飾一個迭代器的好方法是什麼?

內部格式中的變量一次可以跨越多行,因此我編寫了一個生成器,用於拉取每行並在完成時生成變量。

爲了避免在從一個統一的差異文件中讀取時必須重寫此函數,我創建了一個生成器,以便在將統一的差異字符傳遞給內部格式驗證器之前從該行中去除統一的差異字符。但是,我陷入了無限循環(無論是在代碼中還是在我的腦海中)。我已經將問題抽象爲以下代碼。我相信有更好的方法來做到這一點。我只是不知道它是什麼。

from collections import Iterable 

def inner_format_validator(inner_item): 
    # Do some validation to inner items 
    return inner_item[0] != '+' 

def inner_gen(iterable): 
    for inner_item in iterable: 
     # Operates only on inner_info type data 
     yield inner_format_validator(inner_item) 

def outer_gen(iterable): 
    class DecoratedGenerator(Iterable): 
     def __iter__(self): 
      return self 
     def next(self): 
      # Using iterable from closure 
      for outer_item in iterable: 
       self.outer_info = outer_item[0] 
       inner_item = outer_item[1:] 
       return inner_item 
    decorated_gen = DecoratedGenerator() 
    for inner_item in inner_gen(decorated_gen): 
     yield inner_item, decorated_gen.outer_info 

if __name__ == '__main__':  
    def wrap(string): 
     # The point here is that I don't know what the first character will be 
     pseudo_rand = len(string) 
     if pseudo_rand * pseudo_rand % 2 == 0: 
      return '+' + string 
     else: 
      return '-' + string 

    inner_items = ["whatever"] * 3 
    # wrap screws up inner_format_validator 
    outer_items = [wrap("whatever")] * 3 
    # I need to be able to 
    # iterate over inner_items 
    for inner_info in inner_gen(inner_items): 
     print(inner_info) 
    # and iterate over outer_items 
    for outer_info, inner_info in outer_gen(outer_items): 
     # This is an infinite loop 
     print(outer_info) 
     print(inner_info) 

任何想法,以更好,更pythonic的方式來做到這一點?

回答

2

我會做一些簡單的,就像這樣:

def outer_gen(iterable): 

    iterable = iter(iterable) 
    first_item = next(iterable) 
    info = first_item[0] 

    yield info, first_item[1:] 

    for item in iterable: 
     yield info, item 

這將執行4條第一線只有一次,然後進入循環併產生你想要什麼。

您可能想在這裏和那裏添加一些try/except到cacth IndexErrors

如果要在他們開始的東西或者反之取值,記住你可以用很多東西從itertools工具箱,特別dropwhiletakewhilechain

>>> import itertools 
>>> l = ['+foo', '-bar', '+foo'] 
>>> list(itertools.takewhile(lambda x: x.startswith('+'), l)) 
['+foo'] 
>>> list(itertools.dropwhile(lambda x: x.startswith('+'), l)) 
['-bar', '+foo'] 
>>> a = itertools.takewhile(lambda x: x.startswith('+'), l) 
>>> b = itertools.dropwhile(lambda x: x.startswith('+'), l) 
>>> list(itertools.chain(a, b)) 
['+foo', '-bar', '+foo'] 

記住你可以創建一個像理解列表生成器,將它們存儲在變量和連鎖它們,就像你會管的Linux命令:

import random 

def create_item(): 
    return random.choice(('+', '-')) + random.choice(('foo', 'bar')) 

random_items = (create_item() for s in xrange(10)) 
added_items = ((i[0], i[1:]) for i in random_items if i.startswith('+')) 
valid_items = ((prefix, line) for prefix, line in added_items if 'foo' in line) 

print list(valid_items) 

有了這一切,你應該能夠找到一些pythonic方式來解決你的問題:-)

+0

我認爲最後一個例子就是我正在尋找的東西。 – 2011-06-15 05:14:38

1

我認爲它會怎麼做,如果你改變DecoratedGenerator的定義,這就是你們希望:

class DecoratedGenerator(Iterable): 
    def __iter__(self): 
     # Using iterable from closure 
     for outer_item in iterable: 
      self.outer_info = outer_item[0] 
      inner_item = outer_item[1:] 
      yield inner_item 

你的原始版本從未終止,因爲它next()方法是無狀態的,將每一個現在是時候返回相同的值調用。你根本不需要有next()方法 - 你可以自己實現__iter__()(就像我做的那樣),然後一切正常。

2

我還是不喜歡這個非常多,但至少它是短一點點更Python:

from itertools import imap, izip 
from functools import partial 

def inner_format_validator(inner_item): 
    return not inner_item.startswith('+') 

inner_gen = partial(imap, inner_format_validator) 

def split(astr): 
    return astr[0], astr[1:] 

def outer_gen(iterable): 
    outer_stuff, inner_stuff = izip(*imap(split, iterable)) 
    return izip(inner_gen(inner_stuff), outer_stuff) 

[編輯] inner_gen()outer_gen()沒有IMAP和部分:

def inner_gen(iterable): 
    for each in iterable: 
     yield inner_format_validator(each) 

def outer_gen(iterable): 
    outer_stuff, inner_stuff = izip(*(split(each) for each in iterable)) 
    return izip(inner_gen(inner_stuff), outer_stuff) 

也許這是一個更好的,雖然不同的解決方案:

def transmogrify(iter_of_iters, *transmogrifiers): 
    for iters in iter_of_iters: 
     yield (
      trans(each) if trans else each 
       for trans, each in izip(transmogrifiers, iters) 
     ) 

for outer, inner in transmogrify(imap(split, stuff), inner_format_validator, None): 
    print inner, outer 
+0

感謝如何使用'functools.partial'的例子。這些工具非常漂亮,但我覺得這個解決方案有點難以遵循。這樣做會有什麼性能好處嗎? – 2011-06-15 05:06:45

+0

一個問題是在'izip(* imap(split,iterable))'中解壓參數'',它將整個分割的迭代器一次解包到內存中。看到我的另一種解決方案,以避免這種情況。我認爲這是相當pythonic。 – pillmuncher 2011-06-15 23:56:33

相關問題