2010-11-16 146 views
12

我有一個Python for循環中,我需要向前看一個項目,看是否需要採取行動處理之前進行。的Python for循環前瞻

for line in file: 
    if the start of the next line == "0": 
     perform pre-processing 
     ... 
    continue with normal processing 
    ... 

有沒有簡單的方法來做到這一點在python中? 我目前的做法是將文件緩衝到一個數組,但是這並不理想,因爲該文件相當大。

回答

16

您可以得到任何可迭代的下一個項目預取與這個食譜:

from itertools import tee, islice, izip_longest 
def get_next(some_iterable, window=1): 
    items, nexts = tee(some_iterable, 2) 
    nexts = islice(nexts, window, None) 
    return izip_longest(items, nexts) 

用法示例:

for line, next_line in get_next(myfile): 
    if next_line and next_line.startswith("0"): 
     ... do stuff 

的代碼可以讓你的window參數,傳入一個較大的值,如果你想要看兩行或更多行。

+0

是否會導致它從文件中讀取兩次,或者是否以某種方式緩衝該行? – Mike 2010-11-16 19:09:12

+3

它只讀一次。請參見['itertoolsmodule.c']中的'teedataobject_getitem'(http://svn.python.org/projects/python/branches/release27-maint/Modules/itertoolsmodule.c) – 2010-11-16 19:14:44

+4

您的'get_next'在itertools receipes中['pairwise'](http://docs.python.org/library/itertools.html#recipes) – 2010-11-16 19:18:46

9

你可以有您儲存前一行和過程,只要你讀過的一行只給你的條件prev_line

喜歡的東西:

prev_line = None 
for line in file: 
    if prev_line is not None and the start of the next line == "0": 
     perform pre-processing on prev_line 
     ... 
    continue with normal processing 
    ... 
    prev_line = line 

您可能需要在必要時做額外的處理最後一行,這取決於你的邏輯。

3

你只需要緩衝一行。

for line in file: 
    if (prevLine is not None): 
    //test line as look ahead and then act on prevLine 
    prevLine = line 
-2

我不是一個Python的專家,但我想像你需要使用2個迴路這一點。 for循環的第一次運行應構建您需要執行特殊操作的索引列表。然後在第二次運行中,您可以將當前索引與列表進行比較,以確定是否需要執行該特殊操作。

+4

要想找到效率較低的方法很困難,儘管它可能存在:)乾杯 – Morlock 2010-11-16 18:59:43

2

這應該工作了。我總是喜歡打電話next在第一輪設置something = None

prev_line = next(the_file) 
for current_line in the_file: 
    if current_line.startswith('0'): 
     do_stuff(prev_line) 
    # continue with normal processing 
    # ... 
    prev_line = current_line 
8

隨着nosklo的答案的線,我傾向於使用以下模式:

從優秀itertools recipes功能pairwise是非常理想的:

from itertools import tee 

def pairwise(iterable): 
    "s -> (s0,s1), (s1,s2), (s2, s3), ..." 
    a, b = tee(iterable) 
    next(b, None) 
    return izip(a, b) 

在代碼中使用它得到我們:

for line, next_line in pairwise(file): 
    if next_line.startswith("0"): 
     pass #perform pre-processing 
     #... 
    pass #continue with normal processing 

通常,對於這種類型的p處理(在迭代中的前瞻),我傾向於使用window function。成對的大小的窗口2.

+0

是什麼讓你認爲你的解決方案更好?我的方法有什麼問題(使用'izip_longest'和'islice')?我的解決方案允許更大的窗口更容易。 – nosklo 2010-11-16 20:08:59

+0

@nosklo:試試吧。我相信你可以做得比我的鏈接更好,因爲我只是在解釋這個概念。我也可以做得更好。請注意,它並不像您想象的那樣微不足道。我不確定我做了什麼來解決你的問題,但我希望我們能爲SO做出最好的單一答案。如果你編輯你的到達那裏,我將會非常高興。 – 2010-11-16 20:21:43

+0

+1不要爭辯。 – Danosaure 2010-11-16 20:40:22

0

more_itertools有幾個lookahead tools的一個特例。這裏我們將演示一些用於處理文件行的工具和抽象函數。鑑於:

f = """\ 
A 
B 
C 
0 
D\ 
""" 
lines = f.splitlines() 

代碼

import more_itertools as mit 


def iter_lookahead(iterable, pred): 
    # Option 1 
    p = mit.peekable(iterable) 
    try: 
     while True: 
      line = next(p) 
      next_line = p.peek() 
      if pred(next_line): 
       # Do something 
       pass 
      else: 
       print(line) 
    except StopIteration: 
     return 


pred = lambda x: x.startswith("0") 
iter_lookahead(lines, pred) 

輸出

A 
B 
0 

下面是包括由@Muhammad Alkarouri提到pairwisewindowed工具等選項使用相同的庫。

# Option 2 
for line, next_line in mit.pairwise(lines): 
    if pred(next_line):    
     # Do something 
     pass 
    else: 
     print(line) 

# Option 3 
for line, next_line in mit.windowed(lines, 2): 
    if pred(next_line):    
     # Do something 
     pass 
    else: 
     print(line) 

後面的選項可以獨立運行或替代先前函數中的邏輯。