Skip to content

nadict

A nested dict with both attribute and item access.

NA stands for Nested and Attribute.

NADict

A nested dict with both attribute and item access.

It is intended to be used with keys that are valid Python identifiers. However, except for string keys containing a dot, there are actually no hard limitations. If a key equals an existing attribute name, attribute access is of cause not possible.

Nested items can be accessed via a dot notation, as shown in the example below.

Examples

n = NADict(a=1, b=NADict(c=3, d=4)) n['a'] 1 n.a 1 n['b.c'] 3 n.b.c 3 n['b.e'] = 5 n.b.e 5

Attributes

dict

Dictionary holding the actial items.

Source code in ontopy/nadict.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
class NADict:
    """A nested dict with both attribute and item access.

    It is intended to be used with keys that are valid Python
    identifiers.  However, except for string keys containing a dot,
    there are actually no hard limitations.  If a key equals an existing
    attribute name, attribute access is of cause not possible.

    Nested items can be accessed via a dot notation, as shown in the
    example below.

    Examples
    --------
    >>> n = NADict(a=1, b=NADict(c=3, d=4))
    >>> n['a']
    1
    >>> n.a
    1
    >>> n['b.c']
    3
    >>> n.b.c
    3
    >>> n['b.e'] = 5
    >>> n.b.e
    5

    Attributes
    ----------
    _dict : dict
        Dictionary holding the actial items.
    """

    def __init__(self, *args, **kw):
        object.__setattr__(self, "_dict", {})
        self.update(*args, **kw)

    def __getitem__(self, key):
        if "." in key:
            key1, key2 = key.split(".", 1)
            return self._dict[key1][key2]
        return self._dict[key]

    def __setitem__(self, key, value):
        if key in (
            "clear",
            "copy",
            "fromkeys",
            "get",
            "items",
            "keys",
            "pop",
            "popitem",
            "setdefault",
            "update",
            "values",
        ):
            raise ValueError(
                f"invalid key {key!r}: must not override supported dict method"
                " names"
            )

        if "." in key:
            key1, key2 = key.split(".", 1)
            if key1 not in self._dict:
                self._dict[key1] = NADict()
            self._dict[key1][key2] = value
        elif key in self._dict:
            if isinstance(self._dict[key], NADict):
                self._dict[key].update(value)
            else:
                self._dict[key] = value
        else:
            if isinstance(value, Mapping):
                self._dict[key] = NADict(value)
            else:
                self._dict[key] = value

    def __delitem__(self, key):
        if "." in key:
            key1, key2 = key.split(".", 1)
            del self._dict[key1][key2]
        else:
            del self._dict[key]

    def __getattr__(self, key):
        if key not in self._dict:
            raise AttributeError(f"No such key: {key}")
        return self._dict[key]

    def __setattr__(self, key, value):
        if key in self._dict:
            self._dict[key] = value
        else:
            object.__setattr__(self, key, value)

    def __delattr__(self, key):
        if key in self._dict:
            del self._dict[key]
        else:
            object.__delattr__(self, key)

    def __len__(self):
        return len(self._dict)

    def __contains__(self, key):
        if "." in key:
            key1, key2 = key.split(".", 1)
            return key2 in self._dict[key1]
        return key in self._dict

    def __iter__(self, prefix=""):
        for key, value in self._dict.items():
            key = f"{prefix}.{key}" if prefix else key
            if isinstance(value, NADict):
                yield from value.__iter__(key)
            else:
                yield key

    def __repr__(self):
        return (
            f"{self.__class__.__name__}("
            f"{', '.join(f'{key}={value!r}' for key, value in self._dict.items())})"  # pylint: disable=line-too-long
        )

    def clear(self):
        """Clear all keys."""
        self._dict.clear()

    def copy(self):
        """Returns a deep copy of self."""
        return copy.deepcopy(self)

    @staticmethod
    def fromkeys(iterable, value=None):
        """Returns a new NADict with keys from `iterable` and values
        set to `value`."""
        res = NADict()
        for key in iterable:
            res[key] = value
        return res

    def get(self, key, default=None):
        """Returns the value for `key` if `key` is in self, else return
        `default`."""
        if "." in key:
            key1, key2 = key.split(".", 1)
            return self._dict[key1].get(key2, default)
        return self._dict.get(key, default)

    def items(self, prefix=""):
        """Returns an iterator over all items as (key, value) pairs."""
        for key, value in self._dict.items():
            key = f"{prefix}.{key}" if prefix else key
            if isinstance(value, NADict):
                yield from value.items(key)
            else:
                yield (key, value)

    def keys(self, prefix=""):
        """Returns an iterator over all keys."""
        for key, value in self._dict.items():
            key = f"{prefix}.{key}" if prefix else key
            if isinstance(value, NADict):
                yield from value.keys(key)
            else:
                yield key

    def pop(self, key, default=None):
        """Removed `key` and returns corresponding value.  If `key` is not
        found, `default` is returned if given, otherwise KeyError is
        raised."""
        if "." in key:
            key1, key2 = key.split(".", 1)
            return self._dict[key1].pop(key2, default)
        return self._dict.pop(key, default)

    def popitem(self, prefix=""):
        """Removes and returns some (key, value). Raises KeyError if empty."""
        item = self._dict.popitem()
        if isinstance(item, NADict):
            key, value = item
            item2 = item.popitem(key)
            self._dict[key] = value
            return item2
        key, value = self._dict.popitem()
        key = f"{prefix}.{key}" if prefix else key
        return (key, value)

    def setdefault(self, key, value=None):
        """Inserts `key` and `value` pair if key is not found.

        Returns the new value for `key`."""
        if "." in key:
            key1, key2 = key.split(".", 1)
            return self._dict[key1].setdefault(key2, value)
        return self._dict.setdefault(key, value)

    def update(self, *args, **kwargs):
        """Updates self with dict/iterable from `args` and keyword arguments
        from `kw`."""
        for arg in args:
            if hasattr(arg, "keys"):
                for _ in arg:
                    self[_] = arg[_]
            else:
                for key, value in arg:
                    self[key] = value
        for key, value in kwargs.items():
            self[key] = value

    def values(self):
        """Returns a set-like providing a view of all style values."""
        return self._dict.values()

clear()

Clear all keys.

Source code in ontopy/nadict.py
135
136
137
def clear(self):
    """Clear all keys."""
    self._dict.clear()

copy()

Returns a deep copy of self.

Source code in ontopy/nadict.py
139
140
141
def copy(self):
    """Returns a deep copy of self."""
    return copy.deepcopy(self)

fromkeys(iterable, value=None) staticmethod

Returns a new NADict with keys from iterable and values set to value.

Source code in ontopy/nadict.py
143
144
145
146
147
148
149
150
@staticmethod
def fromkeys(iterable, value=None):
    """Returns a new NADict with keys from `iterable` and values
    set to `value`."""
    res = NADict()
    for key in iterable:
        res[key] = value
    return res

get(key, default=None)

Returns the value for key if key is in self, else return default.

Source code in ontopy/nadict.py
152
153
154
155
156
157
158
def get(self, key, default=None):
    """Returns the value for `key` if `key` is in self, else return
    `default`."""
    if "." in key:
        key1, key2 = key.split(".", 1)
        return self._dict[key1].get(key2, default)
    return self._dict.get(key, default)

items(prefix='')

Returns an iterator over all items as (key, value) pairs.

Source code in ontopy/nadict.py
160
161
162
163
164
165
166
167
def items(self, prefix=""):
    """Returns an iterator over all items as (key, value) pairs."""
    for key, value in self._dict.items():
        key = f"{prefix}.{key}" if prefix else key
        if isinstance(value, NADict):
            yield from value.items(key)
        else:
            yield (key, value)

keys(prefix='')

Returns an iterator over all keys.

Source code in ontopy/nadict.py
169
170
171
172
173
174
175
176
def keys(self, prefix=""):
    """Returns an iterator over all keys."""
    for key, value in self._dict.items():
        key = f"{prefix}.{key}" if prefix else key
        if isinstance(value, NADict):
            yield from value.keys(key)
        else:
            yield key

pop(key, default=None)

Removed key and returns corresponding value. If key is not found, default is returned if given, otherwise KeyError is raised.

Source code in ontopy/nadict.py
178
179
180
181
182
183
184
185
def pop(self, key, default=None):
    """Removed `key` and returns corresponding value.  If `key` is not
    found, `default` is returned if given, otherwise KeyError is
    raised."""
    if "." in key:
        key1, key2 = key.split(".", 1)
        return self._dict[key1].pop(key2, default)
    return self._dict.pop(key, default)

popitem(prefix='')

Removes and returns some (key, value). Raises KeyError if empty.

Source code in ontopy/nadict.py
187
188
189
190
191
192
193
194
195
196
197
def popitem(self, prefix=""):
    """Removes and returns some (key, value). Raises KeyError if empty."""
    item = self._dict.popitem()
    if isinstance(item, NADict):
        key, value = item
        item2 = item.popitem(key)
        self._dict[key] = value
        return item2
    key, value = self._dict.popitem()
    key = f"{prefix}.{key}" if prefix else key
    return (key, value)

setdefault(key, value=None)

Inserts key and value pair if key is not found.

Returns the new value for key.

Source code in ontopy/nadict.py
199
200
201
202
203
204
205
206
def setdefault(self, key, value=None):
    """Inserts `key` and `value` pair if key is not found.

    Returns the new value for `key`."""
    if "." in key:
        key1, key2 = key.split(".", 1)
        return self._dict[key1].setdefault(key2, value)
    return self._dict.setdefault(key, value)

update(*args, **kwargs)

Updates self with dict/iterable from args and keyword arguments from kw.

Source code in ontopy/nadict.py
208
209
210
211
212
213
214
215
216
217
218
219
def update(self, *args, **kwargs):
    """Updates self with dict/iterable from `args` and keyword arguments
    from `kw`."""
    for arg in args:
        if hasattr(arg, "keys"):
            for _ in arg:
                self[_] = arg[_]
        else:
            for key, value in arg:
                self[key] = value
    for key, value in kwargs.items():
        self[key] = value

values()

Returns a set-like providing a view of all style values.

Source code in ontopy/nadict.py
221
222
223
def values(self):
    """Returns a set-like providing a view of all style values."""
    return self._dict.values()
Back to top