【问题标题】:python loop exceptionpython循环异常
【发布时间】:2013-01-22 23:58:54
【问题描述】:

嘿,我是 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
    ...
    ...
    ...
    ...
    // 
    

谢谢

【问题讨论】:

  • 你得到了哪种类型的异常?

标签: python exception python-2.7 while-loop


【解决方案1】:

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 循环吗?”,即使答案是“不,不是没有让事情变得不那么可读。”

附带说明一下,您可以通过将 try/finally 替换为 with 语句来简化事情。而不是这个:

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

你可以这样做:

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

或者,如果output 不是普通文件,您仍然可以将最后四行替换为:

with contextlib.closing(output):
    blah blah

【讨论】:

  • @RocketDonkey:我有点担心这是一个不好的例子,因为状态机足够复杂,值得拥有,但又足够简单,可以手动展开,这在现实生活中很少发生。 (另外,我没有想到好的状态名称,所以我只是称它们为 0、1、2,这并不是一个好的建议……)所以,在意识到这一点之前,我几乎放弃了它并将所有内容重写为协程“如果您使用的是 3.0-3.2,请改为执行此操作;如果您使用的是 2.6-2.7,请执行此操作,……”将是代码的 3 倍……
【解决方案2】:

当您调用line = it.next() 时,当没有任何剩余时,会引发StopIteration 异常:

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

这每次都会在您的代码中发生,因为您是在块的末尾调用它,因此在循环有机会返回并发现line 为空之前引发异常。作为创可贴修复,您可以执行以下操作,捕获StopIteration 异常并将其传递出去(因为这表明它已完成):

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

【讨论】:

  • +1。做到这一点可能很棘手,这就是为什么作为一般规则,围绕it.next()while 循环通常应该重写为for line in it: 循环。但是,当您尝试在循环中将迭代器推进额外时间时,这个一般建议不起作用,因此您需要这样的东西,或者更大的重写。
  • @abarnert 哈,作为一个建议,我已经加入了for 循环,然后我实际上看看她/他正在尝试做什么,并意识到它不会完全实现他们想要什么:)
【解决方案3】:

1/ 无异常处理

为避免处理异常StopIteration,您应该查看处理序列的 Pythonic 方式(正如 Abarnert 提到的):

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

2/ 捕捉信息

另外,您可以尝试使用正则表达式来捕捉您的信息模式。您确实知道第一行的确切表达式。然后你想捕捉名字并将它与一些可接受的名字列表进行比较。最后,您正在寻找下一个//。您可以构建一个包含换行符的正则表达式,并使用一个组来捕获您要检查的名称,

(...)

匹配括号内的任何正则表达式, 表示组的开始和结束;组的内容 可以在执行匹配后检索,并且可以匹配 稍后在带有 \number 特殊序列的字符串中,描述 以下。要匹配文字 '(' 或 ')',请使用 ( 或 ),或将它们括起来 在字符类中:[(] [)]。

以下是 Python 文档中组的正则表达式使用示例

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

【讨论】:

    【解决方案4】:

    您可以明确忽略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)
    

    【讨论】:

      【解决方案5】:

      我喜欢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
      ...
      ...
      ...
      ...
      

      【讨论】:

        猜你喜欢
        • 2018-03-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多