我有一个列表推导过滤列表:
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)中,我问是否有更好的方法来循环避免列表重复。
社区回答我,以避免列表修改,并使用适用的列表推导,如果我忽略异常问题。
您的观点是否有替代解决方案?我可以使用列表推导以这种方式管理异常吗?在这种情况下并使用大名单(我必须考虑哪些大?)我必须找到另一种选择吗?
答案 0 :(得分:9)
我会使用一点辅助功能:
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
类进行任何更改,代码的整体结构非常简单。
答案 1 :(得分:1)
几条评论:
首先,列表推导语法[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()
答案 2 :(得分:0)
不是复制列表和删除元素,而是从空白列表开始,并根据需要添加成员。像这样:
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
语法不是那么漂亮,但它或多或少与列表推导相同,没有任何不必要的删除。
在列表末尾以外的任何位置添加或删除元素都会很慢,因为当你删除某些东西时,它需要遍历它后面的每个项目并从索引中减去一个,并且相同添加东西的东西,除了它需要添加到索引。更新后面的所有元素的位置。
答案 3 :(得分:0)
你可以定义一个调用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