You may have misunderstood the behavior of the for statement in ~~ python, so if you have any idea why, please let me know. ~~ Solved.
Iterator that returns the same string three times
iter.py
# coding: utf-8
class Iterator(object):
def __init__(self):
self.counter = 3
def __iter__(self):
return self
def next(self):
self.counter -= 1
if self.counter < 0:
raise StopIteration
return "Hooray!"
Verification
>>> from iter import Iterator
>>> instance = Iterator()
>>> iterator = iter(instance)
>>> print iterator.next()
Hooray!
>>> print iterator.next()
Hooray!
>>> print iterator.next()
Hooray!
>>> print iterator.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "iter.py", line 13, in next
raise StopIteration
StopIteration
for statement can also be used
>>> for i in Iterator():
... print i
...
Hooray!
Hooray!
Hooray!
RunTimeIterator
which inherits ʻIterator and replaces the
next` method after advancing the original iteration only once at initialization
iter.py
# coding: utf-8
class Iterator(object):
def __init__(self):
self.counter = 3
def __iter__(self):
return self
def next(self):
self.counter -= 1
if self.counter < 0:
raise StopIteration
return "Hooray!"
class RunTimeIterator(Iterator):
def __init__(self):
super(RunTimeIterator, self).__init__()
for i in self:
print i
break
self.next = self.alter_next
def alter_next(self):
self.counter -= 1
if self.counter < 0:
raise StopIteration
return "Noshi!"
Verification
>>> from iter import RunTimeIterator
>>> instance = RunTimeIterator()
Hooray!
>>> iterator = iter(instance)
>>> print iterator.next()
Noshi!
>>> print iterator.next()
Noshi!
>>> print iterator.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "iter.py", line 28, in alter_next
raise StopIteration
StopIteration
Now, if you move this with a for statement,
>>> for i in RunTimeIterator():
... print i
...
Hooray!
Hooray!
Hooray!
It doesn't work for some reason.
~~ I don't know.
It seems to be a problem that occurs when trying to replace the next
method directly, in the above example it works normally if you hold the returned string in an instance variable and replace it, and more generally the next
method implements another method If you try to return and replace it, it will work. ~~
Actually, if you use the next ()
function of build-in without calling the next
method directly, the same phenomenon as the for statement will occur.
>>> from iter import RunTimeIterator
>>> instance = RunTimeIterator()
Hooray!
>>> iterator = iter(instance)
>>> print next(iterator)
Hooray!
>>> print next(iterator)
Hooray!
>>> print next(iterator)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "iter.py", line 12, in next
raise StopIteration
StopIteration
As for the behavior of the for statement, it seems that iterating is performed using the next ()
function without directly calling the next
method of the iterator object.
The reason why the next ()
function was created in the first place is that you don't have to call the next
method directly like you use ʻiter ()or
len ()instead of calling
iter or
lendirectly. It seems that it is because of this (in addition, is it convenient to rename it to
next in python3?) [^ 1], so it seems that it is not recommended to call the
next` method of the instance directly.
After all, the problem is that the build-in'next () returns not the instance's
nextmethod at the time of the iteration, but the
next` method at the time of class definition. Apparently, the iterator implementation is set when the class is defined [^ 2].
Even if you replace the next
method of the instance, it will not be reflected in the actual iterator, and if you directly rewrite the next
method of the class, it will work.
python
class RunTimeIterator(Iterator):
def __init__(self):
super(RunTimeIterator, self).__init__()
for i in self:
print i
break
# self.next = self.alter_next
self.__class__.next = self.alter_next
def alter_next(self):
self.counter -= 1
if self.counter < 0:
raise StopIteration
return "Noshi!"
However, if you replace it at the class level, it will of course be like this when you create two or more instances.
>>> from iter import RunTimeIterator
>>> for i in RunTimeIterator():
... print i
...
Hooray!
Noshi!
Noshi!
>>> for i in RunTimeIterator():
... print i
...
Noshi!
Noshi!
Noshi!
The next
method cannot be replaced directly, so make sure the next
method returns an implementation of another method and replace it when you want to replace it.
In the following example, the next
method returns the result of the _next
method, and when you want to replace the next
method, you replace the _next
method.
class RunTimeIterator(Iterator):
def __init__(self):
super(RunTimeIterator, self).__init__()
for i in self:
print i
break
self._next = self.alter_next
def next(self):
"""Real`next`It ’s a method, but the contents are`_next`
"""
return self._next()
def _next(self):
"""Substantial`next`Method
As the initial state of the parent`next`Try to use the method.
`_next`Instead of defining a method`__init__`To
self._next = super(RunTimeIterator, self).next
It is the same even if it is written.
"""
return super(RunTimeIterator, self).next()
def alter_next(self):
self.counter -= 1
if self.counter < 0:
raise StopIteration
return "Noshi!"
>>> from iter import RunTimeIterator
>>> for i in RunTimeIterator():
... print i
...
Hooray!
Noshi!
Noshi!
>>> for i in RunTimeIterator():
... print i
...
Hooray!
Noshi!
Noshi!
Originally, I want to write a parser that parses the area corresponding to the header from a file of a certain format and then retrieves only the necessary data for each line in a class that inherits file
, and replaces the next
method. I ran into this problem while trying something.
Recommended Posts