This article has participated in the activity of “New person creation Ceremony”, and started the road of digging gold creation together
preface
Last time we briefly shared iterators and generators, this time we’ll take a closer look at the concepts and their use, hopefully to help you.
Custom iterators
First, let’s look at how to define a custom iterator. The class of a custom iterator needs the following components.
(1) Iter and next magic methods need to be defined in the class. (2) Iter magic method returns the object itself. (3) The next method returns the next data. If there is no data, StopIteration is raised.
class Test:
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration
return self.counter
t = Test()
print(next(t))
print(next(t))
print(next(t))
1
2
StopIteration
Copy the code
Of course, we can call the iterator object directly using the for loop.
for i in t:
print(i)
1
2
Copy the code
How exactly does the for loop execute? The for loop first calls the iter magic method of the object, returns an iterator object, and then keeps calling the next magic method (the exception stops the loop).
The generator
We learned earlier that if a function has the yield keyword, that function is a generator.
def func():
yield 1
yield 2
f = func()
print(next(f))
print(next(f))
1
2
Copy the code
Inside the generator object is an object created by the generator class, which also declares iter and next magic methods.
Generators also fully comply with the rules of iterator declarations, so generators are a special kind of iterator.
iterable
Finally, let’s talk about iterables, and lists, as we all know, are iterables.
l = [1, 2, 3]
for i in l:
print(i)
Copy the code
Strings, dictionaries, things that can loop, are all iterable. The definition is that if a class has iter magic methods and returns iterators, the object created by that class is an iterable.
class Test:
def __init__(self):
self.counter = 0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == 3:
raise StopIteration
return self.counter
class Foo:
def __iter__(self):
return Test()
foo = Foo()
for item in foo:
print(item)
1
2
Copy the code
Foo is an iterable, and when we use the for loop, we call iter’s magic method, which returns an iterator, and then we call the next magic method over and over again.
We can verify that a list is an iterable, so it should have iter methods and no next methods.
l = [1, 2, 3]
print(dir(l))
#['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Copy the code
print(dir(l.__iter__()))
Copy the code
After the iter method is called, there are iter and next methods.
Custom range functions
So with all that we’ve learned, let’s define a range function to reinforce what we’ve learned.
class RangeIter:
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == self.num:
raise StopIteration
return self.counter
class Xrange:
def __init__(self, maxnum):
self.maxnum = maxnum
def __iter__(self):
return RangeIter(self.maxnum)
for i in Xrange(10):
print(i)
Copy the code
Generators can also do this.
class Xrange:
def __init__(self, maxnum):
self.maxnum = maxnum
def __iter__(self):
counter = 0
while counter < self.maxnum:
yield counter
counter += 1
for i in Xrange(5):
print(i)
Copy the code
That’s it for today. See you next time