在评估网络响应的代码中,我想从多个列表中压缩元素。然而,迭代器的元素是
dict
的。
因此,我想用 dict
来填充缺失值,但每个生成的元素都应该有它自己的 dict
实例。
以下代码按
itertools.zip_longest
对每个列表中的元素进行分组。
只要指定了不可变的fillvalue
,就没有问题。
import collections
import itertools
l1 = [{"a": 100}, {"b": 200}, {"c": 300}]
l2 = [{"d": 400}]
ll = list(itertools.zip_longest(l1, l2, fillvalue=0))
print(ll)
->
[({'a': 100}, {'d': 400}), ({'b': 200}, 0), ({'c': 300}, 0)]
现在,当指定一个可变的
fillvalue
时,所有 fillvalue
共享同一个实例,因此更改一个实例就会更改所有实例:
import collections
import itertools
l1 = [{"a": 100}, {"b": 200}, {"c": 300}]
l2 = [{"d": 400}]
ll = list(itertools.zip_longest(l1, l2, fillvalue=dict()))
ll[1][1]["x"] = 150
print(ll)
->
[({'a': 100}, {'d': 400}), ({'b': 200}, {'x': 150}), ({'c': 300}, {'x': 150})]
为了防止所有字典共享同一个实例,我使用了
copy.deepcopy
:
import collections
import copy
import itertools
l1 = [{"a": 100}, {"b": 200}, {"c": 300}]
l2 = [{"d": 400}]
ll = list(itertools.zip_longest(l1, l2, fillvalue=copy.deepcopy(dict())))
ll[1][1]["x"] = 150
print(ll)
->
[({'a': 100}, {'d': 400}), ({'b': 200}, {'x': 150}), ({'c': 300}, {'x': 150})]
因此,来自
dict
的所有 fillvalue
仍然共享同一个实例。
我想补充一点,假设填充值为
ll = [item or dict() for item in itertools.zip_longest(l1, l2)]
,None
都不起作用。
那么,我怎样才能让每个
fillvalue
独一无二呢?
您可以使用哨兵值并将
zip_longest
包装在另一个管道中。
例如:
sentinel = object()
l = list(tuple({} if x is sentinel else x for x in items) for items in zip_longest(l1, l2, fillvalue=sentinel))
对于此示例,您可以使用
None
而不是自定义 sentinel
对象。
您无法教
zip_longest
为您复制dict
。您必须自己复制它们(这也会使原始值与 l2
分离),例如:
ll = [(d1, d2.copy()) for d1, d2 in itertools.zip_longest(l1, l2, fillvalue={})]
或者只是根据需要从头开始制作它们(这会将空的
dict
与 l2
分离,留下其余的别名,这可能是也可能不是您想要的):
ll = [(d1, d2 or {}) for d1, d2 in itertools.zip_longest(l1, l2, fillvalue=False)]
完美保留别名的替代方案会更冗长:
ll = [(d1, {} if d2 is None else d2) for d1, d2 in itertools.zip_longest(l1, l2, fillvalue=None)]
如果
l1
可能比 l2
短,您可以对每个列表组件中的 d1
应用类似的转换。