From 9d63b557eaa91ac949205403963146cd173269d8 Mon Sep 17 00:00:00 2001 From: Guzz-T Date: Sun, 1 Mar 2026 22:03:37 +0100 Subject: [PATCH 1/2] Iterate through field-data-dict using definition-field-pairs instead of index-field pairs. This loosens the previously fixed connection between an index and a single data field. --- luxtronik/cfi/interface.py | 12 ++--- luxtronik/collections.py | 36 +++++--------- luxtronik/data_vector.py | 8 ++-- luxtronik/scripts/__init__.py | 14 +++--- luxtronik/shi/interface.py | 6 +-- luxtronik/shi/vector.py | 2 +- tests/cfi/test_cfi_calculations.py | 9 ++-- tests/cfi/test_cfi_interface.py | 6 +-- tests/cfi/test_cfi_parameters.py | 19 +++++--- tests/cfi/test_cfi_visibilities.py | 9 ++-- tests/fake/fake_luxtronik.py | 12 ++--- tests/shi/test_shi_interface.py | 4 +- tests/shi/test_shi_vector.py | 18 ++++--- tests/test_collections.py | 77 +++++++++++++++++------------- tests/test_data_vector.py | 8 +++- tests/test_socket_interaction.py | 6 +-- 16 files changed, 132 insertions(+), 114 deletions(-) diff --git a/luxtronik/cfi/interface.py b/luxtronik/cfi/interface.py index 5a536d51..01007831 100644 --- a/luxtronik/cfi/interface.py +++ b/luxtronik/cfi/interface.py @@ -162,20 +162,20 @@ def _write_and_read(self, parameters, data): return self._read(data) def _write(self, parameters): - for index, field in parameters.data.items(): + for definition, field in parameters.items(): if field.write_pending: field.write_pending = False value = field.raw - if not isinstance(index, int) or not field.check_for_write(parameters.safe): + if not isinstance(definition.index, int) or not field.check_for_write(parameters.safe): LOGGER.warning( "%s: Parameter id '%s' or value '%s' invalid!", self._host, - index, + definition.index, value, ) continue - LOGGER.info("%s: Parameter '%d' set to '%s'", self._host, index, value) - self._send_ints(LUXTRONIK_PARAMETERS_WRITE, index, value) + LOGGER.info("%s: Parameter '%d' set to '%s'", self._host, definition.index, value) + self._send_ints(LUXTRONIK_PARAMETERS_WRITE, definition.index, value) cmd = self._read_int() LOGGER.debug("%s: Command %s", self._host, cmd) val = self._read_int() @@ -275,7 +275,7 @@ def _parse(self, data_vector, raw_data): undefined = {i for i in range(0, raw_len)} # integrate the data into the fields - for pair in data_vector.data.pairs(): + for pair in data_vector.data.items(): definition, field = pair # skip this field if there are not enough data next_idx = definition.index + definition.count diff --git a/luxtronik/collections.py b/luxtronik/collections.py index 9190bdf7..8ead2a59 100644 --- a/luxtronik/collections.py +++ b/luxtronik/collections.py @@ -205,8 +205,7 @@ def integrate_data(self, raw_data, num_bits, data_offset=-1): class LuxtronikFieldsDictionary: """ - Dictionary that behaves like the earlier data vector dictionaries (index-field-dictionary), - with the addition that obsolete fields are also supported and can be addressed by name. + Dictionary that maps definitions, names or indices to added fields. Aliases are also supported. """ @@ -228,15 +227,12 @@ def __getitem__(self, def_field_name_or_idx): return self.get(def_field_name_or_idx) def __len__(self): - return len(self._def_lookup._index_dict) + """Return the number of added fields.""" + return len(self._pairs) def __iter__(self): - """ - Iterate over all non-obsolete indices. If an index is assigned multiple times, - only the index of the preferred definition will be output. - """ - all_related_defs = self._def_lookup._index_dict.values() - return iter([d.index for d in self._pairs if d in all_related_defs]) + """Return the iterator over all definitions related to the added fields.""" + return iter([d for d, _ in self._pairs]) def __contains__(self, def_field_name_or_idx): """ @@ -265,26 +261,16 @@ def __contains__(self, def_field_name_or_idx): return def_field_name_or_idx in self._def_lookup def values(self): - """ - Iterator for all added non-obsolete fields. If an index is assigned multiple times, - only the field of the preferred definition will be output. - """ - all_related_defs = self._def_lookup._index_dict.values() - return iter([f for d, f in self._pairs if d in all_related_defs]) + """Return the iterator over all added fields.""" + return iter([f for _, f in self._pairs]) def items(self): - """ - Iterator for all non-obsolete index-field-pairs (list of tuples with - 0: index, 1: field) contained herein. If an index is assigned multiple times, - only the index-field-pair of the preferred definition will be output. - """ - all_related_defs = self._def_lookup._index_dict.values() - return iter([(d.index, f) for d, f in self._pairs if d in all_related_defs]) + """Return the iterator over all added definition-field-pairs.""" + return iter(self._pairs) + @property def pairs(self): - """ - Return all definition-field-pairs contained herein. - """ + """Return all definition-field-pairs contained herein.""" return self._pairs @property diff --git a/luxtronik/data_vector.py b/luxtronik/data_vector.py index 9569eca8..30897ef0 100644 --- a/luxtronik/data_vector.py +++ b/luxtronik/data_vector.py @@ -78,12 +78,12 @@ def _name_lookup(self, name): obsolete_entry = self._obsolete.get(name, None) if obsolete_entry: return None, obsolete_entry - for index, entry in self._data.items(): - check_result = entry.check_name(name) + for definition, field in self._data.items(): + check_result = field.check_name(name) if check_result == LUXTRONIK_NAME_CHECK_PREFERRED: - return index, None + return definition.index, None elif check_result == LUXTRONIK_NAME_CHECK_OBSOLETE: - return index, entry.name + return definition.index, field.name return None, None def _lookup(self, target, with_index=False): diff --git a/luxtronik/scripts/__init__.py b/luxtronik/scripts/__init__.py index c9072d23..86e259bf 100644 --- a/luxtronik/scripts/__init__.py +++ b/luxtronik/scripts/__init__.py @@ -25,8 +25,8 @@ def print_dump_row(number, field): def dump_fields(data_vector): print_dump_header(f"{data_vector.name}s") - for index, field in data_vector.data.items(): - print_dump_row(index, field) + for definition, field in data_vector.data.items(): + print_dump_row(definition.index, field) def print_watch_header(screen, caption): cols, _ = screen.get_visible_size() @@ -41,11 +41,11 @@ def get_watch_row(short_name, number, prev_field, this_field): return text def update_changes(changes, prev_data_vector, this_data_vector): - for index, this_field in this_data_vector.data.items(): + for definition, this_field in this_data_vector.data.items(): short_name = this_data_vector.name[:4] - key = f"{short_name}_{str(index).zfill(5)}" - prev_field = prev_data_vector.get(index) + key = f"{short_name}_{str(definition.index).zfill(5)}" + prev_field = prev_data_vector.get(definition.name) if this_field.raw != prev_field.raw: - changes[key] = get_watch_row(short_name, index, prev_field, this_field) + changes[key] = get_watch_row(short_name, definition.index, prev_field, this_field) elif key in changes: - changes[key] = get_watch_row(short_name, index, prev_field, None) + changes[key] = get_watch_row(short_name, definition.index, prev_field, None) diff --git a/luxtronik/shi/interface.py b/luxtronik/shi/interface.py index 238d41ab..689bd312 100644 --- a/luxtronik/shi/interface.py +++ b/luxtronik/shi/interface.py @@ -469,12 +469,12 @@ def _collect_fields(self, blocks_list, data_vector, definitions, read_not_write) # Trial-and-error mode: Add a block for every field blocks = ContiguousDataBlockList(definitions.name, read_not_write) if (read_not_write == READ): - for definition, field in data_vector.data.pairs(): + for definition, field in data_vector.data.items(): # _prepare_read_field will never fail, no need to call it #if self._prepare_read_field(definition, field): blocks.append_single(definition, field) else: - for definition, field in data_vector.data.pairs(): + for definition, field in data_vector.data.items(): if self._prepare_write_field(definition, field, data_vector.safe, None): blocks.append_single(definition, field) if len(blocks) > 0: @@ -488,7 +488,7 @@ def _collect_fields(self, blocks_list, data_vector, definitions, read_not_write) else: blocks = ContiguousDataBlockList(definitions.name, read_not_write) # Organize data into contiguous blocks - for definition, field in data_vector.data.pairs(): + for definition, field in data_vector.data.items(): if self._prepare_write_field(definition, field, data_vector.safe, None): blocks.collect(definition, field) if len(blocks) > 0: diff --git a/luxtronik/shi/vector.py b/luxtronik/shi/vector.py index 45eda78c..497d4331 100644 --- a/luxtronik/shi/vector.py +++ b/luxtronik/shi/vector.py @@ -265,7 +265,7 @@ def update_read_blocks(self): """ if not self._read_blocks_up_to_date: self._read_blocks.clear() - for definition, field in self._data.pairs(): + for definition, field in self._data.pairs: self._read_blocks.collect(definition, field) self._read_blocks_up_to_date = True diff --git a/tests/cfi/test_cfi_calculations.py b/tests/cfi/test_cfi_calculations.py index 18556308..ffc4b6f0 100644 --- a/tests/cfi/test_cfi_calculations.py +++ b/tests/cfi/test_cfi_calculations.py @@ -4,6 +4,7 @@ from luxtronik import Calculations from luxtronik.datatypes import Base +from luxtronik.definitions import LuxtronikDefinition class TestCalculations: @@ -23,10 +24,10 @@ def test_data(self): # The Value must be a fields # The key can be an index assert isinstance(data[0], Base) - for k in data: - assert isinstance(k, int) + for d in data: + assert isinstance(d, LuxtronikDefinition) for v in data.values(): assert isinstance(v, Base) - for k, v in data.items(): - assert isinstance(k, int) + for d, v in data.items(): + assert isinstance(d, LuxtronikDefinition) assert isinstance(v, Base) diff --git a/tests/cfi/test_cfi_interface.py b/tests/cfi/test_cfi_interface.py index 6a839468..e842d0f2 100644 --- a/tests/cfi/test_cfi_interface.py +++ b/tests/cfi/test_cfi_interface.py @@ -46,7 +46,7 @@ def test_parse(self): parameters[20].write_pending = True parameters[40].write_pending = True lux._parse(parameters, t) - for definition, field in parameters.data.pairs(): + for definition, field in parameters.data.items(): if definition.index > n: assert field.raw is None assert not field.write_pending @@ -55,7 +55,7 @@ def test_parse(self): calculations[20].write_pending = True calculations[40].write_pending = True lux._parse(calculations, t) - for definition, field in calculations.data.pairs(): + for definition, field in calculations.data.items(): if definition.index > n: assert field.raw is None assert not field.write_pending @@ -64,7 +64,7 @@ def test_parse(self): visibilities[20].write_pending = True visibilities[40].write_pending = True lux._parse(visibilities, t) - for definition, field in visibilities.data.pairs(): + for definition, field in visibilities.data.items(): if definition.index > n: assert field.raw is None assert not field.write_pending \ No newline at end of file diff --git a/tests/cfi/test_cfi_parameters.py b/tests/cfi/test_cfi_parameters.py index 11a6dcf4..20a327e1 100644 --- a/tests/cfi/test_cfi_parameters.py +++ b/tests/cfi/test_cfi_parameters.py @@ -4,6 +4,7 @@ from luxtronik import Parameters from luxtronik.datatypes import Base +from luxtronik.definitions import LuxtronikDefinition class TestParameters: @@ -27,12 +28,12 @@ def test_data(self): # The Value must be a fields # The key can be an index assert isinstance(data[0], Base) - for k in data: - assert isinstance(k, int) + for d in data: + assert isinstance(d, LuxtronikDefinition) for v in data.values(): assert isinstance(v, Base) - for k, v in data.items(): - assert isinstance(k, int) + for d, v in data.items(): + assert isinstance(d, LuxtronikDefinition) assert isinstance(v, Base) def test_get(self): @@ -68,11 +69,15 @@ def test___iter__(self): """Test cases for __iter__""" parameters = Parameters() - for i, p in parameters: - if i == 0: + for d, p in parameters.items(): + if d.index == 0: assert p.name == "ID_Transfert_LuxNet" - elif i == 1: + assert d is parameters.data.def_dict.get(0) + assert p is parameters.get(0) + elif d.index == 1: assert p.name == "ID_Einst_WK_akt" + assert d is parameters.data.def_dict.get(1) + assert p is parameters.get(1) else: break diff --git a/tests/cfi/test_cfi_visibilities.py b/tests/cfi/test_cfi_visibilities.py index 43f9af8a..37b3cac1 100644 --- a/tests/cfi/test_cfi_visibilities.py +++ b/tests/cfi/test_cfi_visibilities.py @@ -4,6 +4,7 @@ from luxtronik import Visibilities from luxtronik.datatypes import Base +from luxtronik.definitions import LuxtronikDefinition class TestVisibilities: @@ -23,10 +24,10 @@ def test_data(self): # The Value must be a fields # The key can be an index assert isinstance(data[0], Base) - for k in data: - assert isinstance(k, int) + for d in data: + assert isinstance(d, LuxtronikDefinition) for v in data.values(): assert isinstance(v, Base) - for k, v in data.items(): - assert isinstance(k, int) + for d, v in data.items(): + assert isinstance(d, LuxtronikDefinition) assert isinstance(v, Base) diff --git a/tests/fake/fake_luxtronik.py b/tests/fake/fake_luxtronik.py index 8aaf38bc..bc9e639f 100644 --- a/tests/fake/fake_luxtronik.py +++ b/tests/fake/fake_luxtronik.py @@ -5,9 +5,9 @@ class FakeLuxtronik(Luxtronik): def __init__(self): LuxtronikAllData.__init__(self) - for idx, field in self.parameters: - field.raw = idx - for idx, field in self.calculations: - field.raw = idx - for idx, field in self.visibilities: - field.raw = idx \ No newline at end of file + for definition, field in self.parameters.items(): + field.raw = definition.index + for definition, field in self.calculations.items(): + field.raw = definition.index + for definition, field in self.visibilities.items(): + field.raw = definition.index \ No newline at end of file diff --git a/tests/shi/test_shi_interface.py b/tests/shi/test_shi_interface.py index fa5ad2be..84afdebe 100644 --- a/tests/shi/test_shi_interface.py +++ b/tests/shi/test_shi_interface.py @@ -1291,7 +1291,7 @@ def check_definitions(self, interface): for d in definitions: assert d.name in vector assert d in interface.holdings - for f in vector: + for f in vector.values(): assert f.name in definitions assert f.name in interface.holdings @@ -1302,7 +1302,7 @@ def check_definitions(self, interface): for d in definitions: assert d.name in vector assert d in interface.inputs - for f in vector: + for f in vector.values(): assert f.name in definitions assert f.name in interface.inputs diff --git a/tests/shi/test_shi_vector.py b/tests/shi/test_shi_vector.py index d6da59c2..90e4f59e 100644 --- a/tests/shi/test_shi_vector.py +++ b/tests/shi/test_shi_vector.py @@ -92,7 +92,7 @@ def test_create(self): data_vector = DataVectorTest(parse_version("1.2")) assert data_vector.version == (1, 2, 0, 0) assert len(data_vector) == 3 - assert len(data_vector._data.pairs()) == 3 + assert len(data_vector._data.pairs) == 3 assert not data_vector._read_blocks_up_to_date assert len(data_vector._read_blocks) == 0 @@ -216,7 +216,7 @@ def test_add(self): field = data_vector.add(def_9a) assert def_9a in data_vector assert len(data_vector) == 3 - assert len(data_vector.data._pairs) == 3 + assert len(data_vector.data.pairs) == 3 assert field.name == 'field_9a' # Get via index (last added) @@ -252,13 +252,15 @@ def test_iter(self): for index, definition in enumerate(data_vector): if index == 0: assert definition.index == 5 - assert definition.name == 'field_5' + assert definition.name == "field_5" if index == 1: assert definition.index == 9 - assert definition.name == 'field_9a' + assert definition.name == "field_9a" if index == 2: assert definition.index == 9 - assert definition.name == 'field_9' + assert definition.name == "field_9" + if index == 3: + assert False for index, field in enumerate(data_vector.values()): if index == 0: @@ -267,6 +269,8 @@ def test_iter(self): assert field.name == 'field_9a' if index == 2: assert field.name == 'field_9' + if index == 3: + assert False for index, (definition, field) in enumerate(data_vector.items()): if index == 0: @@ -281,6 +285,8 @@ def test_iter(self): assert definition.index == 9 assert definition.name == 'field_9' assert field.name == 'field_9' + if index == 3: + assert False def test_set(self): data_vector = DataVectorTest(parse_version("1.1.2")) @@ -406,7 +412,7 @@ def test_version_none(self): assert len(data_vector) == 3 data_vector.add(10) # field_9a alias assert len(data_vector) == 4 - assert len(data_vector._data._pairs) == 4 + assert len(data_vector.data.pairs) == 4 class TestHoldings: diff --git a/tests/test_collections.py b/tests/test_collections.py index a1f41adc..39a3688a 100644 --- a/tests/test_collections.py +++ b/tests/test_collections.py @@ -234,31 +234,31 @@ def test_init(self): def test_add(self): d = LuxtronikFieldsDictionary() assert len(d) == 0 - assert len(d.pairs()) == 0 + assert len(d.pairs) == 0 u = LuxtronikDefinition.unknown(1, "test", 0) f = u.create_field() d.add(u, f) assert len(d) == 1 - assert len(d._pairs) == 1 - assert d._pairs[0].definition is u - assert d._pairs[0].field is f + assert len(d.pairs) == 1 + assert d.pairs[0].definition is u + assert d.pairs[0].field is f u = LuxtronikDefinition.unknown(2, "test", 0) f = u.create_field() d.add(u, f) assert len(d) == 2 - assert len(d._pairs) == 2 - assert d._pairs[1].definition is u - assert d._pairs[1].field is f + assert len(d.pairs) == 2 + assert d.pairs[1].definition is u + assert d.pairs[1].field is f u = LuxtronikDefinition.unknown(0, "test", 0) f = u.create_field() d.add_sorted(u, f) assert len(d) == 3 assert len(d._pairs) == 3 - assert d._pairs[0].definition is u - assert d._pairs[0].field is f + assert d.pairs[0].definition is u + assert d.pairs[0].field is f def create_instance(self): d = LuxtronikFieldsDictionary() @@ -284,8 +284,8 @@ def create_instance(self): def test_len(self): d, _, _ = self.create_instance() # 3 different indices - assert len(d) == 3 - assert len(d.pairs()) == 4 + assert len(d) == 4 + assert len(d.pairs) == 4 def test_get_contains(self): d, u, f = self.create_instance() @@ -311,42 +311,55 @@ def test_get_contains(self): def test_iter(self): d, _, _ = self.create_instance() - for idx, key in enumerate(d): + for idx, d in enumerate(d): if idx == 0: - assert key == 1 + assert d.name == "unknown_test_1" + assert d.index == 1 if idx == 1: - assert key == 2 + assert d.name == "unknown_test_2" + assert d.index == 2 if idx == 2: - assert key == 3 + assert d.name == "base2" + assert d.index == 2 + if idx == 3: + assert d.name == "base3" + assert d.index == 3 def test_values(self): d, _, _ = self.create_instance() - for idx, value in enumerate(d.values()): + for idx, f in enumerate(d.values()): if idx == 0: - assert type(value) is Unknown - assert value.name == "unknown_test_1" + assert type(f) is Unknown + assert f.name == "unknown_test_1" if idx == 1: - assert type(value) is Base - assert value.name == "base2" + assert type(f) is Unknown + assert f.name == "unknown_test_2" if idx == 2: - assert type(value) is Base - assert value.name == "base3" + assert type(f) is Base + assert f.name == "base2" + if idx == 3: + assert type(f) is Base + assert f.name == "base3" def test_items(self): d, _, _ = self.create_instance() - for idx, (key, value) in enumerate(d.items()): + for idx, (d, f) in enumerate(d.items()): if idx == 0: - assert key == 1 - assert type(value) is Unknown - assert value.name == "unknown_test_1" + assert d.index == 1 + assert type(f) is Unknown + assert f.name == "unknown_test_1" if idx == 1: - assert key == 2 - assert type(value) is Base - assert value.name == "base2" + assert d.index == 2 + assert type(f) is Unknown + assert f.name == "unknown_test_2" if idx == 2: - assert key == 3 - assert type(value) is Base - assert value.name == "base3" + assert d.index == 2 + assert type(f) is Base + assert f.name == "base2" + if idx == 3: + assert d.index == 3 + assert type(f) is Base + assert f.name == "base3" class MyTestClass: pass diff --git a/tests/test_data_vector.py b/tests/test_data_vector.py index 9a13632e..b8cae68f 100644 --- a/tests/test_data_vector.py +++ b/tests/test_data_vector.py @@ -6,6 +6,7 @@ from luxtronik.data_vector import DataVector from luxtronik.datatypes import Base +from luxtronik.definitions import LuxtronikDefinition class ObsoleteDataVector(DataVector): @@ -18,8 +19,13 @@ class ObsoleteDataVector(DataVector): def __init__(self): super().__init__() + d = LuxtronikDefinition({ + "index": 0, + "type": Base, + "names": ["foo", "bar"], + }, "type", 0) self._data = { - 0: Base(["foo", "bar"]), + d: d.create_field() } diff --git a/tests/test_socket_interaction.py b/tests/test_socket_interaction.py index 9d001f21..34e8ebdf 100644 --- a/tests/test_socket_interaction.py +++ b/tests/test_socket_interaction.py @@ -34,7 +34,7 @@ def check_data_vector(self, data_vector): fct = fake_calculation_value elif type(data_vector) is Visibilities: fct = fake_visibility_value - for d, f in data_vector.data.pairs(): + for d, f in data_vector.items(): # get raw data raw = [fct(idx) for idx in range(d.index, d.index + d.count)] temp_field = d.create_field() @@ -49,8 +49,8 @@ def clear_luxtronik_data(self, lux): self.clear_data_vector(lux.visibilities) def clear_data_vector(self, data_vector): - for idx, entry in data_vector: - entry.raw = 0 + for d, f in data_vector.items(): + f.raw = 0 def test_luxtronik_socket_interface(self): host = "my_heatpump" From a9d55aae92cef1d1dd3830cddb9c17f86f448bd6 Mon Sep 17 00:00:00 2001 From: Guzz-T Date: Sun, 1 Mar 2026 22:16:26 +0100 Subject: [PATCH 2/2] Move the iterator functions into the data vector base class --- luxtronik/data_vector.py | 31 +++++++++++++++++++++++++++---- luxtronik/shi/vector.py | 12 ------------ 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/luxtronik/data_vector.py b/luxtronik/data_vector.py index 30897ef0..45077dbd 100644 --- a/luxtronik/data_vector.py +++ b/luxtronik/data_vector.py @@ -29,10 +29,6 @@ def __init__(self): """Initialize DataVector class.""" self._data = LuxtronikFieldsDictionary() - def __iter__(self): - """Iterator for the data entries.""" - return iter(self._data.items()) - @property def data(self): """ @@ -55,6 +51,19 @@ def __setitem__(self, def_name_or_idx, value): """ return self.set(def_name_or_idx, value) + def __len__(self): + """ + Forward the `LuxtronikFieldsDictionary.__len__` method. + Please check its documentation. + """ + return len(self._data) + + def __iter__(self): + """ + Forward the `LuxtronikFieldsDictionary.__iter__` method. + Please check its documentation. + """ + return iter(self._data) def __contains__(self, def_field_name_or_idx): """ @@ -63,6 +72,20 @@ def __contains__(self, def_field_name_or_idx): """ return def_field_name_or_idx in self._data + def values(self): + """ + Forward the `LuxtronikFieldsDictionary.values` method. + Please check its documentation. + """ + return self._data.values() + + def items(self): + """ + Forward the `LuxtronikFieldsDictionary.items` method. + Please check its documentation. + """ + return iter(self._data.items()) + def _name_lookup(self, name): """ Try to find the index using the given field name. diff --git a/luxtronik/shi/vector.py b/luxtronik/shi/vector.py index 497d4331..c191799b 100644 --- a/luxtronik/shi/vector.py +++ b/luxtronik/shi/vector.py @@ -143,12 +143,6 @@ def empty(cls, version=LUXTRONIK_LATEST_SHI_VERSION, safe=True): obj._init_instance(version, safe) return obj - def __len__(self): - return len(self._data.pairs()) - - def __iter__(self): - return iter([definition for definition, _ in self._data.pairs()]) - # properties and access methods ############################################### @@ -156,12 +150,6 @@ def __iter__(self): def version(self): return self._version - def values(self): - return iter([field for _, field in self._data.pairs()]) - - def items(self): - return iter(self._data.pairs()) - # Find, add and alias methods #################################################