---------------------------------------- > Date: Fri, 9 Oct 2015 01:14:05 -0500 > From: eryk...@gmail.com > To: tutor@python.org > Subject: Re: [Tutor] FrozenDict > > On 10/8/15, Steven D'Aprano <st...@pearwood.info> wrote: >> >> That's one solution, but it is certainly possible for the class to be >> its own iterator, in which case it needs to follow two rules: >> >> (1) self.__next__() needs to return the next value, or raise >> StopIteration; >> >> (2) self.__iter__() needs to return self; >> >> and of course like all dunder methods __next__ and __iter__ need to be >> defined on the class itself, not on the instance. > > Except this is generally a bad way to iterate a reiterable. Without a > separate iterator, there's no simple way to maintain state for > concurrent iterations. > > file types are an exception. A file is reiterable (i.e. by seeking > back to 0), but the OS file pointer makes a file naturally an > iterator. Thus getting concurrent iterations of a disk file requires > separate file objects that have separate OS file pointers. > > FrozenDict.next is an example of what not to do: > > def next(self): > try: > value = self.__kwargs.items()[self.__counter][0] > except IndexError: > raise StopIteration > self.__counter += 1 > return value > > In Python 2 this iterates the dict's keys by creating a list of (key, > value) tuples -- every time next() is called. In Python 3, you'd have > to explicitly create the list using list(self.__kwargs.items()). The > design also lacks support for concurrent iterations, and not even > multiple iterations since __counter isn't reset in __iter__.
Hi Erysun, Steven and others. Eww, I see now that this indeed horribly inefficient. > > The simple approach is to have __iter__ return an instance of the > wrapped dict's iterator. There's no reason to reinvent the wheel. Plus > in the non-frozen case, the built-in dict iterators are smart enough > to raise an error when the dict is modified, since it's assumed that > any modification to the hash table invalidates the iteration. I don't understand what you mean with this. It reads like super(FrozenDict, self).__iter__(), but that can't be correct because the supper class is an abstract class. Anyway, I rewrote the code (__repr__ is omitted for brevity): from collections import Mapping import sys class FrozenDict(Mapping): """A dictionary that does not support item assignment after initialization >>> fd = FrozenDict(a=1, b=2) >>> fd["a"] 1 >>> fd["a"] = 777 # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: 'FrozenDict' object does not support item assignment >>> sorted(fd.items()) [('a', 1), ('b', 2)] >>> sorted(fd.keys()) ['a', 'b'] """ def __init__(self, **kwargs): self.__kwargs = kwargs self.__init__ = None def __getitem__(self, key): return self.__kwargs[key] def __iter__(self): return self.__kwargs.iterkeys() if sys.version_info.major> 2: __iter__ = lambda self: iter(self.__kwargs.keys()) # Python 3: I am assuming this approach is more efficient than using try-except. def __len__(self): return len(self.__kwargs) Curious what you think about this code. By the way, why is a TyperError raised when one tries to set an item? An AttributeError seems more logical. if __name__ == "__main__": import doctest doctest.testmod() As always, thanks a lot! Best wishes, Albert-Jan _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor