allthedict

Keys, attributes, indices… Nobody cares as long as you don’t use integers for keys!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class allthedict(dict):
    """The only downside is, integer keys are not valid"""

    def __getitem__(self, key):
        if isinstance(key, int):
            key = list(self.keys())[key]
        return super().__getitem__(key)

    def __setitem__(self, key, value):
        if isinstance(key, int):
            try:
                key = list(self.keys())[key]
            except IndexError:
                raise ValueError('keys cannot be integers', key)
        super().__setitem__(key, value)

    def __getattr__(self, attr):
        if attr in self.keys():
            return super().__getitem__(attr)
        raise AttributeError(f'{self} has not attribute {attr}')

    def __setattr__(self, attr, value):
        self.__setitem__(attr, value)

Here’s some tests:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import pytest

from allthedict import allthedict


def test_allthedict():
    d = allthedict()

    d['key'] = 'value'
    assert d.key == 'value'
    assert d['key'] == 'value'
    assert d[0] == 'value'

    with pytest.raises(ValueError):
        d[1] = 'anotherval'

    assert 1 not in d.keys()

    with pytest.raises(AttributeError):
        d.anotherkey

    d.anotherkey = 'anotherval'
    assert d.anotherkey == 'anotherval'
    assert d[1] == 'anotherval'
    assert d['anotherkey'] == 'anotherval'

    assert len(d) == 2

    assert d[-1] == 'anotherval'

    d[0] = 'hello'
    assert d.key == 'hello'

Now do an import allthedict as dict and enjoy not caring!