Reputation: 2240
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
Upvotes: 0
Views: 787
Reputation: 188034
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
Upvotes: 3
Reputation: 365737
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.
Upvotes: 4
Reputation: 2240
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)
Upvotes: 0
Reputation:
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
>>>
Upvotes: 3