2011-06-13 23 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

相关问题