2011-06-13 11 views
5

Bir biçimi birleşik diff düzeltme eki içinden doğrulamayı içeren bir sorun üzerinde çalışıyorum.Bir iteratörün python'da sonraki değerini değiştirmek için dekore etmenin iyi bir yolu nedir?

İç biçimdeki değişkenler, bir seferde birden çok satıra yayılabilir, bu yüzden her satırı çeken ve tamamlandığında değişkeni veren bir jeneratör yazdım.

Birleşik bir diff dosyasından okurken bu işlevi yeniden yazmak zorunda kalmamak için, birleştirilmiş farklı karakterleri satırdan iç format doğrulayıcıya geçirmeden önce kesmek için bir jeneratör oluşturdum. Ancak, sonsuz bir döngüde (hem kodda hem de kafamda) sıkışıp kalıyorum. Aşağıdaki kodla ilgili problemi özetledim. Eminim bunu yapmanın daha iyi bir yolu vardır. Sadece ne olduğunu bilmiyorum.

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) 

Bunu yapmak için daha iyi, daha pythonik bir yolla ilgili herhangi bir fikir var mı? onun next() yöntem vatansız olması nedeniyle

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 

Orijinal versiyon sonlandırıldı asla ve aynı değerini oldu her zaman döneceğini:

cevap

2

Böyle basit bir şey yapacağını: Bu, sadece bir kez 4 ilk satırları yürütmek sonra döngüye girebilir ve ne istediğinizi verecektir

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 

.

Muhtemelen try/except numaralı cacth IndexErrors'u buraya ve buraya eklemek istersiniz.

>>> 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'] 

Ve unutmayın:

onlar bir şey ya da tam tersine ile başlarken değerlerini almak isterseniz, itertools araç kutusundan bir sürü kullanmak ve özellikle dropwhile, takewhile ve chain içinde olabilir hatırlıyorum bütün bunlarla

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) 

, sen olmalıdır: onları, anlama listeleri gibi jeneratörler oluşturmak değişkenler saklayabilirsiniz ve zincir anlamına, sadece boru linux komutları olur gibi Sorununuzu çözmek için pythonik bir yol bulabilirim :-)

+0

Son örnekte aradığım şey bence. –

1

Ben şuna DecoratedGenerator tanımını değiştirirseniz amaçlanan yapacak düşünüyorum aradı. Yine de bir next() yöntemine sahip olmanıza gerek kalmadı; __iter__()'u (yaptığım gibi) kendiniz uygulayabilirsiniz, ve sonra her şey iyi çalışıyor.

2

Hala çok bu sevmiyorum, ama en azından daha kısa ve daha pythonic biraz:

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() ve outer_gen() olmadan [DÜZENLE] imap ve kısmi:

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) 

Belki de bu daha iyi olsa da, daha iyi bir çözümdür:

+0

'functools.partial' kullanımıyla ilgili örnek için teşekkür ederiz.Araçlar oldukça şık, ama bu çözümü takip etmek biraz zor. Bu şekilde yapmanın herhangi bir performans avantajı olur mu? –

+0

Sorunlardan biri, bölünmüş yinelenebilir yinelemeyi bir kerede belleğe açan paket ('imap (bölünmüş, yinelenebilir)) türündeki paketin açılmasıdır. Bunu önleyen bir alternatif için diğer çözümüme bakın. Bence oldukça pythonic. – pillmuncher

İlgili konular