List linked to dictionary values

I'm working with a complex structure of nested dictionaries and I want to use some functions that take a list as an argument.

Is there any way of getting a list of values from a dictionary but keeping both linked in a way that if I modify one the other also gets modified?

Let me illustrate with an example:

# I have this dictionary
d = {'first': 1, 'second': 2}

# I want to get a list like this
print(l) # shows [1, 2]

# I want to modify the list and see the changes reflected in d
l[0] = 23
print(l) # shows [23, 2]
print(d) # shows {'fist': 23, 'second': 2}

Is there any way of achieve something similar?

Upvotes: 0

Views: 41

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123520

You'd have to create a custom sequence object that wraps the dictionary, mapping indices back to keys to access get or set values:

from collections.abc import Sequence

class ValueSequence(Sequence):
    def __init__(self, d):
        self._d = d
    def __len__(self):
        return len(self._d)
    def _key_for_index(self, index):
        # try to avoid iteration over the whole dictionary
        if index >= len(self):
            raise IndexError(index)
        return next(v for i, v in enumerate(self._d) if i == index)
    def __getitem__(self, index):
        key = self._key_for_index(index)
        return self._d[key]
    def __setitem__(self, index, value):
        key = self._key_for_index(index)
        self._d[key] = value
    def __repr__(self):
        return repr(list(self._d.values()))

The object doesn't support deletions, insertions, appending or extending. Only manipulation of existing dictionary values are supported. The object is also live; if you alter the dictionary, the object will reflect those changes directly.

Demo:

>>> d = {'first': 1, 'second': 2}
>>> l = ValueSequence(d)
>>> print(l)
[1, 2]
>>> l[0] = 23
>>> print(l)
[23, 2]
>>> print(d)
{'first': 23, 'second': 2}
>>> d['second'] = 42
>>> l
[23, 42]

These are not necessarily efficient, however.

Inheriting from the Sequence ABC gives you a few bonus methods:

>>> l.index(42)
1
>>> l.count(42)
1
>>> 23 in l
True
>>> list(reversed(l))
[42, 23]

Take into account that dictionaries are unordered; the above object will reflect changes to the dictionary directly, and if such changes result in a different ordering then that will result in a different order of the values too. The order of a dictionary does remain stable if you don't add or remove keys, however.

Upvotes: 2

Related Questions