Mein __len __ () funktioniert mit Len () und List () für benutzerdefinierte Klasse nicht richtigPython

Python-Programme
Anonymous
 Mein __len __ () funktioniert mit Len () und List () für benutzerdefinierte Klasse nicht richtig

Post by Anonymous »

Folgendes ist eine vereinfachte Version von dem, was ich versuche zu tun (die tatsächliche Implementierung hat eine Reihe von Nuancen): < /p>
>>

Code: Select all

class SideDict(MutableMapping, dict):  # isinstance(obj, (MutableMapping, dict)) should return True
"""
The purpose of this special dict is to side-attach another dict. A key
and its value from main dict are preferred over same key in the
side-dict. If only a key is not present in main dict, then it is used
from the side-dict.
"""

# The starting SideDict instance will have side_dict=None, a subsequent
# SideDict instance can use the first instance as its side_dict.
def __init__(self, data, side_dict: SideDict | None):
self._store = dict(data)
self._side_dict = side_dict
# Also other stuff

# Also implements __bool__, __contains__, __delitem__, __eq__, __getitem__,
# __missing__, __or__, __setitem__ and others.

def __iter__(self):
self._iter_keys_seen = []
self._iter = self._store.__iter__()
return self

def __next__(self):
# (Just a summary of the code as it is somewhat complex and large.
#  The logic seems to be working fine.)
# - First run self._iterator.__next__(). All keys are appended to
#   self._iter_keys_seen.
# - The moment StopIteration exception is raised, silence it and
#   change iterator to self._side_dict.__iter__().
# - If a key in this iterator is already in self._iter_keys_seen (i.e.
#   in self._store), then skip and go to next key.
# - Else return it and also add it to self._iter_keys_seen.
# - If StopIteration is raised now, don't silence it.

def __len__(self):
return len([k for k in self])  # Its not the most efficient, but
# I don't know any other way.

sd_0 = SideDict(data=..., side_dict=None)
sd_1 = SideDict(data=..., side_dict=sd_0)
sd_2 = SideDict(data=..., side_dict=sd_1)

print(len(sd_0), len(sd_1), len(sd_2))  # all work fine
print(list(sd_0))  # ! Here is the problem, shows empty list `[]` !
< /code>
Beim Einlegen von Print () < /code> s, hier ist das, was ich beobachtet habe: < /p>

[*]list()
löst obj .__ iter __ () zuerst. Ich verstehe vage, dass dies getan wird, um eine optimale Länge der Liste zuzuweisen.

Code: Select all

[k for k in self]
), es löst OBJ .__ ITER __ () .
[*] gefolgt von OBJ .__ Weiter __ () Mehrfach wie es iteriert, wenn obj._store und obj._side_dict . Stopitation , Listen-Verständnis in obj .__ len __ () endet.
Hier beginnt das Problem. list () scheint obj .__ als nächstes __ () unmittelbar nach Ende der Beendigung von obj .__ len __ () zu sein, und es trifft erneut Stopitation . Es gibt keinen obj .__ iter __ () . Und so ist das Endergebnis eine leere Liste! Mein __len __ () verwendet selbst einen Iterator, so dass die beiden denselben Iterator verwenden. Und dann wird dieser Iterator in obj .__ len __ () konsumiert, und für die äußere Liste () ist nichts mehr zu konsumieren. Bitte korrigieren Sie mich, wenn ich falsch liege. Die bessere Lösung
~ 6x schneller als Lösung 2 für die Liste (Side_dict_with_5_items)

Code: Select all

class SideDict(...):
def __iter__(self):
yield from self._store

if self._side_dict is not None:
for key in self._side_dict :
if key not in self._store:
yield key

# Removed __next__(...), all other stuff remains the same
< /code>
[b] 2. Eine andere Arbeitslösung [/b]
Nur für das Konzept 
class SideDict(...):
def __iter__(self):
return SideDictIterator(self)

# Removed __next__(...), all other stuff remains the same

class SideDictIterator:
def __init__(self, side_dict: SideDict):
self._side_dict = side_dict

self._iter_keys_seen = []
self._iter = self._side_dict._store.__iter__()

def __iter__(self):
return self

def __next__(self):
# Exactly the same stuff that was in SideDict.__next__(),
# except using self._side_dict instead of self

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post