How to implement “next” for a dictionary object to be iterable?

五迷三道 提交于 2019-12-22 00:44:12

问题


I've got the following wrapper for a dictionary:

class MyDict:
    def __init__(self):
        self.container = {}

    def __setitem__(self, key, value):
        self.container[key] = value

    def __getitem__(self, key):
        return self.container[key]

    def __iter__(self):
        return self

    def next(self):
        pass

dic = MyDict()
dic['a'] = 1
dic['b'] = 2

for key in dic:
    print key

My problem is that I don't know how to implement the next method to make MyDict iterable. Any advice would be appreciated.


回答1:


Dictionaries are themselves not an iterator (which can only be iterated over once). You usually make them an iterable, an object for which you can produce multiple iterators instead.

Drop the next method altogether, and have __iter__ return an iterable object each time it is called. That can be as simple as just returning an iterator for self.container:

def __iter__(self):
    return iter(self.container)

If you must make your class an iterator, you'll have to somehow track a current iteration position and raise StopIteration once you reach the 'end'. A naive implementation could be to store the iter(self.container) object on self the first time __iter__ is called:

def __iter__(self):
    return self

def next(self):
    if not hasattr(self, '_iter'):
        self._iter = iter(self.container)
    return next(self._iter)

at which point the iter(self.container) object takes care of tracking iteration position for you, and will raise StopIteration when the end is reached. It'll also raise an exception if the underlying dictionary was altered (had keys added or deleted) and iteration order has been broken.

Another way to do this would be to just store in integer position and index into list(self.container) each time, and simply ignore the fact that insertion or deletion can alter the iteration order of a dictionary:

_iter_index = 0

def __iter__(self):
    return self

def next(self):
    idx = self._iter_index
    if idx is None or idx >= len(self.container):
        # once we reach the end, all iteration is done, end of.
        self._iter_index = None
        raise StopIteration()
    value = list(self.container)[idx]
    self._iter_index = idx + 1
    return value

In both cases your object is then an iterator that can only be iterated over once. Once you reach the end, you can't restart it again.




回答2:


If you want to be able to use your dict-like object inside nested loops, for example, or any other application that requires multiple iterations over the same object, then you need to implement an __iter__ method that returns a newly-created iterator object.

Python's iterable objects all do this:

>>> [1, 2, 3].__iter__()
<listiterator object at 0x7f67146e53d0>
>>> iter([1, 2, 3]) # A simpler equivalent
<listiterator object at 0x7f67146e5390>

The simplest thing for your objects' __iter__ method to do would be to return an iterator on the underlying dict, like this:

def __iter__(self):
    return iter(self.container)

For more detail than you probably will ever require, see this Github repository.



来源:https://stackoverflow.com/questions/38700734/how-to-implement-next-for-a-dictionary-object-to-be-iterable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!