缩小字典列表的生成器
Generator for a shrinking list of dictionaries
我刚开始使用 python 编程,非常喜欢生成器的概念。
所以我有一个字典列表,每个字典都包含一个 ID 和一个状态,例如前两个元素可能如下所示
lst =[{ "id":1, "status": "new"}, {"id": 2, "status"="finished"}]
实际列表最多可包含 10 个项目。每个 id 对应一个作业,通常作业完成的顺序是随机的。
我想检查作业是否已完成,如果已完成则进行处理。如果没有完成,(由于随机性)我想检查另一个作业,直到所有作业都完成(通常它们会在某个时间完成)
所以对我来说,这听起来像是生成器的一个很好的用途,尤其是在管道中(尽管效率在这里可能并不重要)。
def gener(lst):
while lst:
for item in lst:
if item["status"] == "finished":
yield lst.pop(list.index(item))
else:
check_and_update_status(item)
检查和更新函数可能如下所示:
def check_and_update_status(item):
item["status"]=finished
所以我的第一个问题是:这种方法是否有意义?或者发电机在这里是一个糟糕的选择。
所以,希望我可以在这里添加这两个问题:
有时列表中的词典可能没有状态键。在那种情况下,我想在 else 分支中执行任务。我怎样才能做到这一点?异常处理?
在一些关于python/programming的文献中我了解到设置和程序应该分开。所以我想知道我是否应该在这里硬编码 "finished"(也许还有 "status")。术语 "finished" 源自外部文件,将来可能会更改。有什么优雅的方法可以绕过这个问题吗?
在迭代过程中改变 list
是不安全的,通常没有得到很好的支持。即使它有效,用 lst.pop(lst.index(item))
做它也是愚蠢的,因为你可以做 for i, item in enumerate(lst):
这样你就可以免费获得索引。您可以采取的一种方法是避免在迭代时删除,只需存储要删除的索引,并在最后使用列表推导式重新生成 lst
。例如:
while lst:
todel = set()
for i, item in enumerate(lst):
if item["status"] == "finished":
todel.add(i)
yield item
else:
check_and_update_status(item)
if todel:
lst = [x for i, x in enumerate(lst) if i not in todel]
如果您有更大的列表并且更频繁地从列表中删除,其他方法可能会更好,但这种方法针对不频繁删除进行了优化,而不会在迭代副作用时注册危险的突变。
为了处理不存在的 "status"
键,您可以使用 item.get("status")
而不是索引语法来避免抛出异常(使用一个参数,get
returns None
如果键不存在,或者你可以传递第二个参数作为默认值)。或者,要获取现有值(如果存在),并设置和获取默认状态(如果尚不存在),请使用 item.setdefault("status", "new")
(其中 "new"
应该是您的默认起始状态)。
因此,例如,要检查和设置,您将使用:
if item.setdefault("status", "new") == "finished":
问题3在一般情况下很难回答;如果您有合适的数据模式,最好使用它而不是在魔术字符串中进行硬编码,但这将取决于您的用例。
我刚开始使用 python 编程,非常喜欢生成器的概念。
所以我有一个字典列表,每个字典都包含一个 ID 和一个状态,例如前两个元素可能如下所示
lst =[{ "id":1, "status": "new"}, {"id": 2, "status"="finished"}]
实际列表最多可包含 10 个项目。每个 id 对应一个作业,通常作业完成的顺序是随机的。
我想检查作业是否已完成,如果已完成则进行处理。如果没有完成,(由于随机性)我想检查另一个作业,直到所有作业都完成(通常它们会在某个时间完成)
所以对我来说,这听起来像是生成器的一个很好的用途,尤其是在管道中(尽管效率在这里可能并不重要)。
def gener(lst):
while lst:
for item in lst:
if item["status"] == "finished":
yield lst.pop(list.index(item))
else:
check_and_update_status(item)
检查和更新函数可能如下所示:
def check_and_update_status(item):
item["status"]=finished
所以我的第一个问题是:这种方法是否有意义?或者发电机在这里是一个糟糕的选择。
所以,希望我可以在这里添加这两个问题:
有时列表中的词典可能没有状态键。在那种情况下,我想在 else 分支中执行任务。我怎样才能做到这一点?异常处理?
在一些关于python/programming的文献中我了解到设置和程序应该分开。所以我想知道我是否应该在这里硬编码 "finished"(也许还有 "status")。术语 "finished" 源自外部文件,将来可能会更改。有什么优雅的方法可以绕过这个问题吗?
在迭代过程中改变 list
是不安全的,通常没有得到很好的支持。即使它有效,用 lst.pop(lst.index(item))
做它也是愚蠢的,因为你可以做 for i, item in enumerate(lst):
这样你就可以免费获得索引。您可以采取的一种方法是避免在迭代时删除,只需存储要删除的索引,并在最后使用列表推导式重新生成 lst
。例如:
while lst:
todel = set()
for i, item in enumerate(lst):
if item["status"] == "finished":
todel.add(i)
yield item
else:
check_and_update_status(item)
if todel:
lst = [x for i, x in enumerate(lst) if i not in todel]
如果您有更大的列表并且更频繁地从列表中删除,其他方法可能会更好,但这种方法针对不频繁删除进行了优化,而不会在迭代副作用时注册危险的突变。
为了处理不存在的 "status"
键,您可以使用 item.get("status")
而不是索引语法来避免抛出异常(使用一个参数,get
returns None
如果键不存在,或者你可以传递第二个参数作为默认值)。或者,要获取现有值(如果存在),并设置和获取默认状态(如果尚不存在),请使用 item.setdefault("status", "new")
(其中 "new"
应该是您的默认起始状态)。
因此,例如,要检查和设置,您将使用:
if item.setdefault("status", "new") == "finished":
问题3在一般情况下很难回答;如果您有合适的数据模式,最好使用它而不是在魔术字符串中进行硬编码,但这将取决于您的用例。