How to implement __iter__(self) for a container object (Python)

后端 未结 9 1956
甜味超标
甜味超标 2020-12-02 08:00

I have written a custom container object.

According to this page, I need to implement this method on my object:

__iter__(self)

Howe

相关标签:
9条回答
  • 2020-12-02 08:18

    The "iterable interface" in python consists of two methods __next__() and __iter__(). The __next__ function is the most important, as it defines the iterator behavior - that is, the function determines what value should be returned next. The __iter__() method is used to reset the starting point of the iteration. Often, you will find that __iter__() can just return self when __init__() is used to set the starting point.

    See the following code for defining a Class Reverse which implements the "iterable interface" and defines an iterator over any instance from any sequence class. The __next__() method starts at the end of the sequence and returns values in reverse order of the sequence. Note that instances from a class implementing the "sequence interface" must define a __len__() and a __getitem__() method.

    class Reverse:
        """Iterator for looping over a sequence backwards."""
        def __init__(self, seq):
            self.data = seq
            self.index = len(seq)
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self.index == 0:
                raise StopIteration
            self.index = self.index - 1
            return self.data[self.index]
    
    >>> rev = Reverse('spam')
    >>> next(rev)   # note no need to call iter()
    'm'
    >>> nums = Reverse(range(1,10))
    >>> next(nums)
    9
    
    0 讨论(0)
  • 2020-12-02 08:19

    One option that might work for some cases is to make your custom class inherit from dict. This seems like a logical choice if it acts like a dict; maybe it should be a dict. This way, you get dict-like iteration for free.

    class MyDict(dict):
        def __init__(self, custom_attribute):
            self.bar = custom_attribute
    
    mydict = MyDict('Some name')
    mydict['a'] = 1
    mydict['b'] = 2
    
    print mydict.bar
    for k, v in mydict.items():
        print k, '=>', v
    

    Output:

    Some name
    a => 1
    b => 2
    
    0 讨论(0)
  • 2020-12-02 08:20

    I normally would use a generator function. Each time you use a yield statement, it will add an item to the sequence.

    The following will create an iterator that yields five, and then every item in some_list.

    def __iter__(self):
       yield 5
       yield from some_list
    

    Pre-3.3, yield from didn't exist, so you would have to do:

    def __iter__(self):
       yield 5
       for x in some_list:
          yield x
    
    0 讨论(0)
  • 2020-12-02 08:20

    In case you don't want to inherit from dict as others have suggested, here is direct answer to the question on how to implement __iter__ for a crude example of a custom dict:

    class Attribute:
        def __init__(self, key, value):
            self.key = key
            self.value = value
    
    class Node(collections.Mapping):
        def __init__(self):
            self.type  = ""
            self.attrs = [] # List of Attributes
    
        def __iter__(self):
            for attr in self.attrs:
                yield attr.key
    

    That uses a generator, which is well described here.

    Since we're inheriting from Mapping, you need to also implement __getitem__ and __len__:

        def __getitem__(self, key):
            for attr in self.attrs:
                if key == attr.key:
                    return attr.value
            raise KeyError
    
        def __len__(self):
            return len(self.attrs)
    
    0 讨论(0)
  • 2020-12-02 08:22

    Another option is to inherit from the appropriate abstract base class from the `collections module as documented here.

    In case the container is its own iterator, you can inherit from collections.Iterator. You only need to implement the next method then.

    An example is:

    >>> from collections import Iterator
    >>> class MyContainer(Iterator):
    ...     def __init__(self, *data):
    ...         self.data = list(data)
    ...     def next(self):
    ...         if not self.data:
    ...             raise StopIteration
    ...         return self.data.pop()
    ...         
    ...     
    ... 
    >>> c = MyContainer(1, "two", 3, 4.0)
    >>> for i in c:
    ...     print i
    ...     
    ... 
    4.0
    3
    two
    1
    

    While you are looking at the collections module, consider inheriting from Sequence, Mapping or another abstract base class if that is more appropriate. Here is an example for a Sequence subclass:

    >>> from collections import Sequence
    >>> class MyContainer(Sequence):
    ...     def __init__(self, *data):
    ...         self.data = list(data)
    ...     def __getitem__(self, index):
    ...         return self.data[index]
    ...     def __len__(self):
    ...         return len(self.data)
    ...         
    ...     
    ... 
    >>> c = MyContainer(1, "two", 3, 4.0)
    >>> for i in c:
    ...     print i
    ...     
    ... 
    1
    two
    3
    4.0
    

    NB: Thanks to Glenn Maynard for drawing my attention to the need to clarify the difference between iterators on the one hand and containers that are iterables rather than iterators on the other.

    0 讨论(0)
  • 2020-12-02 08:27

    usually __iter__() just return self if you have already define the next() method (generator object):

    here is a Dummy example of a generator :

    class Test(object):
    
        def __init__(self, data):
           self.data = data
    
        def next(self):
            if not self.data:
               raise StopIteration
            return self.data.pop()
    
        def __iter__(self):
            return self
    

    but __iter__() can also be used like this: http://mail.python.org/pipermail/tutor/2006-January/044455.html

    0 讨论(0)
提交回复
热议问题