2013-01-22 256 views
3

嘿,我是一個Python新手,我需要一些幫助。當循環結束它總是拋出通知循環停止的異常蟒蛇循環異常

try: 
    it = iter(cmLines) 
    line=it.next() 
    while (line): 
    if ("INFERNAL1/a" in line) or ("HMMER3/f" in line) : 
     title = line 
     line = it.next() 
     if word2(line) in namesList: //if second word in line is in list 
     output.write(title) 
     output.write(line) 
     line = it.next() 
     while ("//" not in line): 
      output.write(line) 
      line = it.next() 
     output.write(line) 
    line = it.next() 
except Exception as e: 
    print "Loop exited becuase:" 
    print type(e) 
    print "at " + line 
finally: 
    output.close() 
  1. :我寫下來下面的代碼。儘管它沒有過早終止。我如何阻止?

  2. 有沒有更好的方法來寫我的代碼?更時尚的東西。我有一個有大量信息的大文件,我只想捕捉我需要的信息。信息每片的格式爲:

    Infernal1/a ... 
    Name someSpecificName 
    ... 
    ... 
    ... 
    ... 
    // 
    

謝謝

+0

您獲得哪種類型的Exception? –

回答

2

RocketDonkey的回答是點上。由於迭代方式的複雜性,因此使用for循環沒有簡單的方法,因此您需要明確處理StopIteration

但是,如果您稍微反思一下這個問題,還有其他解決方法。例如,一個簡單的狀態機:

try: 
    state = 0 
    for line in cmLines: 
     if state == 0: 
      if "INFERNAL1/a" in line or "HMMER3/f" in line: 
       title = line 
       state = 1 
     elif state == 1: 
      if word2(line) in NamesList: 
       output.write(title) 
       output.write(line) 
       state = 2 
      else: 
       state = 0 
     elif state == 2: 
      output.write(line) 
      if '//' in line: 
       state = 0 
except Exception as e: 
    print "Loop exited becuase:" 
    print type(e) 
    print "at " + line 
finally: 
    output.close() 

或者,你可以寫一個發生器功能委託給子發生器(如果你在3.3是通過yield from foo(),通過for x in foo(): yield x如果不是),或各種其他的可能性,特別是如果你在更高的層次上重新思考你的問題。

你想在這裏做什麼,但它通常至少價值思考「我可以把這個while環和兩個明確的next電話接入for循環?」,即使答案原來是這也許不是「不,沒有讓事情變得不可讀。」

作爲一個方面說明,您可以通過用with語句替換try/finally來簡化操作。取而代之的是:

output = open('foo', 'w') 
try: 
    blah blah 
finally: 
    output.close() 

你可以只是這樣做:

with open('foo', 'w') as output: 
    blah blah 

或者,如果output是不是一個正常的文件,你仍然可以取代過去的4行:

with contextlib.closing(output): 
    blah blah 
+0

+ 1,狀態機例子很有趣。 – RocketDonkey

+0

@RocketDonkey:我有點擔心這是一個糟糕的例子,因爲狀態機足夠複雜,足以勝任但足夠簡單的手動展開,而這在現實生活中很少是真實的。 (另外,我沒有想到各州的好名字,所以我只是稱它們爲0,1,2,這並不是很好的建議......)所以,我幾乎將它廢棄並將所有內容重寫爲協程,然後才意識到「如果你使用的是3.0-3.2,則改爲使用;如果你使用的是2.6-2.7,那麼......」將會是代碼的3倍...... – abarnert

1

當你調用line = it.next(),當時沒有什麼留下了StopIteration exeception上升:

>>> l = [1, 2, 3] 
>>> i = iter(l) 
>>> i.next() 
1 
>>> i.next() 
2 
>>> i.next() 
3 
>>> i.next() 
Traceback (most recent call last): 
    File "<ipython-input-6-e590fe0d22f8>", line 1, in <module> 
    i.next() 
StopIteration 

這將每次都會在您的代碼中發生,因爲您在塊的末尾調用它,所以在循環有機會繞回並發現之前引發異常爲空。作爲一個創可貼修復,你可以做這樣的事情,在那裏你趕上StopIteration異常,並通過它出來(因爲這表示它完成):

# Your code... 
except StopIteration: 
    pass 
except Exception as e: 
    print "Loop exited becuase:" 
    print type(e) 
    print "at " + line 
finally: 
    output.close() 
+0

+1。正確地做這件事可能會很棘手,這就是爲什麼一般來說,it.next()周圍的'while'循環通常應該被重寫爲'for line in:'循環。但是當你試圖在循環中額外增加迭代器時,這個一般性的建議不起作用,所以你需要類似的東西或者更大的重寫。 – abarnert

+0

@abarnert哈,我已經在一個'for'循環中作爲一個建議,然後我真的看了看他/他正在試圖做什麼,並意識到它不會達到他們想要的:) – RocketDonkey

0

我喜歡Parser Combinators,因爲它們導致了更多的聲明式編程風格。

例如與Parcon庫:

from string import letters, digits 
from parcon import (Word, Except, Exact, OneOrMore, 
        CharNotIn, Literal, End, concat) 

alphanum = letters + digits 

UntilNewline = Exact(OneOrMore(CharNotIn('\n')) + '\n')[concat] 
Heading1 = Word(alphanum + '/') 
Heading2 = Word(alphanum + '.') 
Name = 'Name' + UntilNewline 
Line = Except(UntilNewline, Literal('//')) 
Lines = OneOrMore(Line) 
Block = Heading1['hleft'] + Heading2['hright'] + Name['name'] + Lines['lines'] + '//' 
Blocks = OneOrMore(Block[dict]) + End() 

,然後使用Alex Martelli's Bunch類:

class Bunch(object): 
    def __init__(self, **kwds): 
     self.__dict__.update(kwds) 

names = 'John', 'Jane' 
for block in Blocks.parse_string(config): 
    b = Bunch(**block) 
    if b.name in names and b.hleft.upper() in ("INFERNAL1/A', 'HMMER3/F"): 
     print ' '.join((b.hleft, b.hright)) 
     print 'Name', b.name 
     print '\n'.join(b.lines) 

鑑於此文件:

Infernal1/a ... 
Name John 
... 
... 
... 
... 
// 
SomeHeader/a ... 
Name Jane 
... 
... 
... 
... 
// 
HMMER3/f ... 
Name Jane 
... 
... 
... 
... 
// 
Infernal1/a ... 
Name Billy Bob 
... 
... 
... 
... 
// 

結果是:

Infernal1/a ... 
Name John 
... 
... 
... 
... 
HMMER3/f ... 
Name Jane 
... 
... 
... 
... 
0

1 /否異常處理

爲了避免處理異常StopIteration,你應該看看Python化的方式來處理序列(如Abarnert mentionned):

it = iter(cmLines) 
for line in it: 
    # do 

2 /捕捉信息

另外,您可能會嘗試使用正則表達式來捕捉您的信息模式。你確實知道第一行的確切表達式。然後你想捕捉名稱並將其與一些可接受的名稱進行比較。最後,你正在尋找下一個//。你可以建立包括換行符一個正則表達式,並使用一組捉要覈對姓名,

(...)

匹配任何正則表達式的括號內, 和指示一組的開始和結束; 組的內容可以在執行匹配之後檢索到,並且可以在後面的字符串中使用\ number特殊序列匹配 ,下面描述了 。要匹配文字'('或')',請在字符類中使用(或)或將它們包含在內:[(] [)]。

這裏是在Python DOC

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") 
>>> m.group(0)  # The entire match 
'Isaac Newton' 
>>> m.group(1)  # The first parenthesized subgroup. 
'Isaac' 
>>> m.group(2)  # The second parenthesized subgroup. 
'Newton' 
>>> m.group(1, 2) # Multiple arguments give us a tuple. 
('Isaac', 'Newton') 

更多關於Regex正則表達式使用組的一個例子。

鏈接

迭代器的next()提出的例外在Python:https://softwareengineering.stackexchange.com/questions/112463/why-do-iterators-in-python-raise-an-exception

0

可以忽略StopIteration明確:

try: 
    # parse file 
    it = iter(cmLines) 
    for line in it: 
     # here `line = next(it)` might raise StopIteration 
except StopIteration: 
    pass 
except Exception as e: 
    # handle exception 

或致電line = next(it, None)和檢查None

要分開考慮,你可以分成兩個部分代碼:

  • 拆分輸入記錄:那你有興趣
from collections import deque 
from itertools import chain, dropwhile, takewhile 

def getrecords(lines): 
    it = iter(lines) 
    headers = "INFERNAL1/a", "HMMER3/f" 
    while True: 
     it = chain([next(it)], it) # force StopIteration at the end 
     it = dropwhile(lambda line: not line.startswith(headers), it) 
     record = takewhile(lambda line: not line.starswith("//"), it) 
     yield record 
     consume(record) # make sure each record is read to the end 

def consume(iterable): 
    deque(iterable, maxlen=0) 
  • 輸出記錄:
from contextlib import closing 

with closing(output): 
    for record in getrecords(cmLines): 
     title, line = next(record, ""), next(record, "") 
     if word2(line) in namesList: 
      for line in chain([title, line], record): 
       output.write(line)