Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Python itertools.zip_longest with mutable fillvalue

In a code that evaluates a web response, I would like to zip elements from several lists. However, the elements of the iterators are dict‘s.
Therefore, I would like to fill up the missing values also with dict‘s, but each generated element should have it’s own dict instance.

The following code groups elements from each list by itertools.zip_longest.
As long as there is a non mutable fillvalue specified, there is no problem.

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

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Now, when a mutable fillvalue is specified, all the fillvalue‘s share the same instance and so changing one, changes all:

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})]

To prevent that all the dicts share the same instance I used 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})]

As a result, still all dict‘s from the fillvalue share the same instance.

I would like to add that ll = [item or dict() for item in itertools.zip_longest(l1, l2)] works neither, assuming a fillvalue of None.

So, how can I make each fillvalue unique?

>Solution :

You can use a sentinel value and wrap zip_longest in another pipeline.

For example:

sentinel = object()
l = list(tuple({} if x is sentinel else x for x in items) for items in zip_longest(l1, l2, fillvalue=sentinel))

For this example, you can probably use None rather than a custom sentinel object.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading