To dynamically replace the next method in python

You may have misunderstood the behavior of the for statement in ~~ python, so if you have any idea why, please let me know. ~~ Solved.

Preparation

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!

Main subject

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.

why?

~~ 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 ()orlen ()instead of callingiter or lendirectly. It seems that it is because of this (in addition, is it convenient to rename it tonext 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 thenext` 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!

solution?

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.

Reference link

Recommended Posts

To dynamically replace the next method in python
In the python command python points to python3.8
How to use the __call__ method in a Python class
How to dynamically define variables in Python
How to dynamically zero pad in Python
How to use the C library in Python
Learn the design pattern "Template Method" in Python
I tried the least squares method in Python
Learn the design pattern "Factory Method" in Python
Next Python in C
Draw graphs in Julia ... Leave the graphs to Python
The trick to write flatten concisely in python
Simplex method (simplex method) in Python
Private method in python
How to get the files in the [Python] folder
Try implementing the Monte Carlo method in Python
I want to display the progress in Python!
How to retrieve the nth largest value in Python
I tried to graph the packages installed in Python
How to get the variable name itself in python
Sort in Python. Next, let's think about the algorithm.
How to get the number of digits in Python
[python] Method to darken RGB value in hexadecimal notation
How to know the current directory in Python in Blender
Determine the threshold using the P tile method in python
Convert the image in .zip to PDF with Python
I want to write in Python! (3) Utilize the mock
[Python] Created a method to convert radix in 1 second
How to use the model learned in Lobe in Python
[Python] How to output the list values in order
To do the equivalent of Ruby's ObjectSpace._id2ref in Python
I want to use the R dataset in python
Python OpenCV tried to display the image in text.
To flush stdout in Python
Download the file in Python
Find the difference in Python
Dynamically import scripts in Python
Login to website in Python
Speech to speech in python [text to speech]
Implement method chain in Python
Dynamically call methods in Python
How to develop in Python
Suppressing method overrides in Python
Post to Slack in Python
I want to replace the variables in the python template file and mass-produce it in another file.
[python] How to check if the Key exists in the dictionary
How to debug the Python standard library in Visual Studio
Change the standard output destination to a file in Python
Various ways to calculate the similarity between data in python
I tried "How to get a method decorated in Python"
Convenient writing method when appending to list continuously in Python
How to get the last (last) value in a list in Python
I tried to implement the mail sending function in Python
Programming to fight in the world ~ 5-1
Programming to fight in the world ~ 5-5,5-6
Leave the troublesome processing to Python
Programming to fight in the world 5-3
[Python] How to do PCA in Python
Getting the arXiv API in Python
Convert markdown to PDF in Python
Save the binary file in Python