简体   繁体   中英

Special use for __iter__ and __next__

If I want to implement this, how can I do that?

I know that every time when we call for i in object:

First go def __iter__(self) then go def __next__(self) , but I don't see iter transfer any argument to next. So, how?

class MyClass(object):
    def __init__(self):
        self.data = {"a":1, "b":2, "c":3, "special":0}


for i in MyClass(): # for i in self.data, if i != "special" yield i
    print(i)

Expected Output:

a
b
c

The point of creating an iterator class, with __iter__ and __next__ special methods, is that it's a class , which means it can store state explicitly, in instance attributes.

For example:

class MyClass(object):
    def __init__(self, *filtered_keys):
        self.data = {"a":1, "b":2, "c":3, "special":0}
        self.filtered_keys = set(filtered_keys)
        self.iterator = iter(self.data)
    def __iter__(self):
        return self
    def __next__(self):
        while True:
            key = next(self.iterator)
            if key not in filtered_keys:
                return key

for i in MyClass("special"):
    print(i)

If you don't want to create an iterator , but just an iterable —that is, something whose __iter__ method doesn't return self , but instead returns some other object with a __next__ (and within an __iter__ that returns itself), you can return anything you want in __iter__ .

def __iter__(self):
    return (key for key in self.data if key != 'special')

Or you can even make __iter__ itself a generator function, in which case it returns a generator:

def __iter__(self):
    for key in self.data:
        if key != 'special':
            yield key

Or, if you prefer:

def __iter__(self):
    yield from (key for key in self if key != 'special')

This way, the state is now inside the __iter__ method and/or the thing it returns.

You can have the __iter__ special method filter out "special" with a generator expression :

>>> class MyClass(object):
...     def __init__(self):
...         self.data = {"a":1, "b":2, "c":3, "special":0}
...     def __iter__(self):
...         return (x for x in self.data if x != "special")
...
>>> for i in MyClass():
...     print(i)
...
a
c
b
>>>

Another example for a custom iteration:

#!/usr/bin/env python

class Custom(object):
    def __init__(self):
        self.data = {"a":1, "b":2, "c":3, "special":0}
        self.current = 0

    def __iter__(self):
        return self

    def next(self):
        key = [k for k, v in self.data.iteritems() if v == self.current]
        if not key:
            raise StopIteration
        self.current += 1
        return key[0]

if __name__ == '__main__':
    obj = Custom()
    for i in obj:
        print(i)

Which prints the keys in data sorted by value:

'special'
'a'
'b'
'c'

As gist: https://gist.github.com/miku/01d50434b232367f8bfd#file-ex-py

I don't like my own implementation. I want make something just iterate the dict keys one time. But this method took all keys out, convert to set, and discard special and iter on the set. It's too complicate. Any better idea?

def __iter__(self):
    a = set(self.data.keys())
    a.discard("special")
    return iter(a)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM