【问题标题】:Can I catch error in a list comprehensions to be sure to loop all the list items我可以在列表推导中捕获错误以确保循环所有列表项吗
【发布时间】:2010-02-10 02:14:07
【问题描述】:

我有一个过滤列表的列表推导:

l = [obj for obj in objlist if not obj.mycond()]

但是对象方法 mycond() 可以引发我必须拦截的异常。我需要在循环结束时收集所有错误以显示哪个对象产生了任何问题,同时我想确保循环所有列表元素。

我的解决方案是:

errors = []
copy = objlist[:]

for obj in copy:
    try:
        if (obj.mycond()):
            # avoiding to touch the list in the loop directly
            objlist.remove(obj) 
    except MyException as err:
        errors = [err]
if (errors):
   #do something

return objlist

在这篇文章 (How to delete list elements while cycling the list itself without duplicate it) 中,我询问是否有更好的方法来循环避免列表重复。

社区回答我避免就地修改列表并使用如果我忽略异常问题时适用的列表推导。

您认为有其他解决方案吗?我可以使用列表推导以这种方式管理异常吗?在这种情况下并使用大列表(我必须考虑什么大?)我必须找到另一种选择?

【问题讨论】:

    标签: python exception list cycle


    【解决方案1】:

    我会使用一些辅助功能:

    def f(obj, errs):
      try: return not obj.mycond()
      except MyException as err: errs.append((obj, err))
    
    errs = []
    l = [obj for obj in objlist if f(obj, errs)]
    if errs:
      emiterrorinfo(errs)
    

    注意,这样你在errs中就有了所有的错误对象每个对应的具体异常,所以诊断可以准确完整;以及您需要的l,并且您的objlist 仍然完好无损,可以进一步使用。不需要列表拷贝,也不需要对obj的类做任何改动,代码整体结构非常简单。

    【讨论】:

      【解决方案2】:

      几个cmets:

      首先,列表解析语法[expression for var in iterable] 确实创建了一个副本。如果您不想创建列表的副本,请使用生成器表达式(expression for var in iterable)

      生成器如何工作?本质上是通过在对象上反复调用next(obj) 直到引发GeneratorExit 异常。

      根据您的原始代码,您似乎仍需要过滤后的列表作为输出。

      因此您可以在几乎没有性能损失的情况下进行模拟:

      l = []
      for obj in objlist:
         try:
            if not obj.mycond()
               l.append(obj)
         except Exception:
            pass
      

      但是,您可以使用生成器函数重新设计所有内容:

      def FilterObj(objlist):
         for obj in objlist:
            try:
               if not obj.mycond()
                  yield obj
            except Exception:
               pass
      

      这样,您可以安全地对其进行迭代,同时无需缓存列表:

      for obj in FilterObj(objlist):
         obj.whatever()
      

      【讨论】:

        【解决方案3】:

        你可以定义一个调用 obj.mycond() 并且捕获异常的 obj 方法

        class obj:
        
            def __init__(self):
                self.errors = []
        
            def mycond(self):
                #whatever you have here
        
            def errorcatcher():
                try:
                    return self.mycond()
                except MyException as err:
                    self.errors.append(err)
                    return False # or true, depending upon what you want
        
        l = [obj for obj in objlist if not obj.errorcatcher()]
        
        errors = [obj.errors for obj in objlist if obj.errors]
        
        if errors:
            #do something
        

        【讨论】:

        • 看来你应该说[obj for obj in objlist if not obj.errorcatcher()]
        【解决方案4】:

        不要复制列表并删除元素,而是从一个空白列表开始,并根据需要添加成员。像这样的:

        errors = []
        newlist = []
        
        for obj in objlist:
            try:
                if not obj.mycond():
                    newlist.append(obj)
            except MyException as err:
                errors.append(err)
        if (errors):
           #do something
        
        return newlist
        

        语法没有那么漂亮,但它或多或少地与列表推导所做的相同,没有任何不必要的删除。

        向列表末尾以外的任何地方添加或删除元素会很慢,因为当您删除某些内容时,它需要 遍历它之后的每个项目并从其索引中减去一个,同样添加东西的东西,除了它需要添加到索引中。更新它之后所有元素的位置。

        【讨论】:

        • 将插入和删除描述为列表是一系列指向列表项的指针(“引用”)会更清楚,并且所有以下指针都需要向上或向下移动记忆。没有需要为每个项目更改的“索引”(除非“索引”只是指“位置”,但阅读您的答案的每个人都不清楚)。
        • @Peter 我想我试图从概念层面解释它,而不是实际上它是如何(最有可能)实现的。如果我要从 OO 的角度实现一个列表而不考虑优化,这就是我的实现方式。
        • @Davy8:“它需要通过......”也许你应该改变那个“需要”。您的 OO 透视图实现将如何处理a = b[c]?遍历b 列表寻找索引等于c 的项目?顺便说一句,在你的代码中写着errors = [err],你不是说errors.append(err)吗?
        • @John 我同意这个词可能太强烈了,我的意思是追加。我会解决的。为了配合我提出的假设,我想我会迭代。如果 python 没有提供本机列表或 dict 类(我意识到超级假设),你将如何在纯 python 中实现一个列表? (其实我很想知道有没有不用指针运算的有效方法,不知道怎么做)
        • @Peter btw 我确实将其更改为更新位置而不是索引,但位置不总是等于索引吗? (或 index+1 取决于你从哪里开始计算位置)我认为 index 是更专业的术语,并且与实现更紧密地联系在一起,而 position 更像是一个外行的术语,并不暗示任何关于实现的内容
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-11
        • 1970-01-01
        • 1970-01-01
        • 2020-12-07
        • 1970-01-01
        相关资源
        最近更新 更多