3

How do I merge the views of the items of two dicts in python?

My use case is: I have two private dictionaries in a class. I want the API to treat them as one, and provide an items method as such. The only way I know of is to combine them then provide a view on the two, but for large dictionaries this seems expensive. I'm thinking of sth like a.items() + b.items()

Note: I don't care about key clashes.

This is what I'd like to improve:

class A:
    _priv1 = {'a': 1, 'b': 2}
    _priv2 = {'c': 1, 'd': 2}

    def items(self):
        return {**self._priv1, **self._priv2}.items()
joel
  • 6,359
  • 2
  • 30
  • 55

4 Answers4

2

You can use ChainMap:

from collections import ChainMap

class A:
    _priv1 = {'a': 1, 'b': 2}
    _priv2 = {'c': 1, 'd': 2}

    def items(self):
        return ChainMap(self._priv1, self._priv2)
Mykola Zotko
  • 15,583
  • 3
  • 71
  • 73
1

Merging views of dictionaries means nothing because a view always reflects the content of the corresponding dictionary.

So to have a different view, either you edit one of your dictionaries or you instantiate a new one (like you did), there is no way around it. See this.

But maybe what you want is itertools.chain to iterate across multiple iterables. This solution doesn't insatiate. Or as other have said collections.ChainMap. I would use chain to iterate and ChainMap to make lookups.

Benoît P
  • 3,179
  • 13
  • 31
1

You can use ChainMap:

A ChainMap groups multiple dicts or other mappings together to create a single, updateable view. If no maps are specified, a single empty dictionary is provided so that a new chain always has at least one mapping.

from collections import ChainMap
context = ChainMap(_priv1, _priv2, ...)

Example:

In [3]: _priv1 = {1: 2, 3: 4}

In [4]: _priv2 = {5: 6, 7: 8}

In [5]: from collections import ChainMap
   ...: context = ChainMap(_priv1, _priv2)

In [6]: context
Out[6]: ChainMap({1: 2, 3: 4}, {5: 6, 7: 8})

In [7]: _priv1.update({9: 10})

In [8]: context
Out[8]: ChainMap({1: 2, 3: 4, 9: 10}, {5: 6, 7: 8})

In [9]: context.get(9)
Out[9]: 10

For your code example, I'd use:

from collections import ChainMap

class A:
    _priv1 = {'a': 1, 'b': 2}
    _priv2 = {'c': 1, 'd': 2}
    _union_dict = ChainMap(_priv1, _priv2)

    @classmethod
    def items(cls):
        return cls._union_dict.items()
Yam Mesicka
  • 6,243
  • 7
  • 45
  • 64
1

You can use chain to combine two or more views. As stated:

Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted.

That way, the data is not copied.

from itertools import chain

class A:
    _priv1 = {'a': 1, 'b': 2}
    _priv2 = {'c': 1, 'd': 2}

    def items(self):
        return chain(self._priv1.items(), self._priv2.items())
moshevi
  • 4,999
  • 5
  • 33
  • 50