Contents This is an explanation article on how to perform iterative processing [^ 1] in a Python class. Each code can be downloaded from the GitHub repository.
[^ 1]: Qiita: Python for statement ~ What is iterable ~
iter_minimum.py
iteration = IterClass(["a", "ab", "abc", "bb", "cc"])
print([v for v in iteration if "a" in v])
# ['a', 'ab', 'abc']
| Execution environment | |
|---|---|
| OS | Windows Subsystem for Linux | 
| Python | version 3.8.5 | 
It can be implemented by the special methods of the class __iter__ [^ 2] and the yield from syntax [^ 3]. Stores a list or tuple and returns it in order from the beginning when called as an iterative process.
[^ 2]: Life with Python: How to use Python's iterator generation class [^ 3]: blanktar: What is the yield from python3.3
iter_minimum.py
class IterClass(object):
    """
    Iterable class.
    Args:
        values (list[object] or tuple(object)): list of values
    Raises:
        TypeError: @values is not a list/tuple
    """
    def __init__(self, values):
        if not isinstance(values, (list, tuple)):
            raise TypeError("@values must be a list or tuple.")
        self._values = values
    def __iter__(self):
        yield from self._values
__bool__)In the above example, I don't feel the significance of creating a new class, so I made an advanced version.
First create the smallest unit ʻUnit` class returned by iterative processing.
iter_advanced.py
class Unit(object):
    """
    The smallest unit.
    """
    def __init__(self, value):
        if not isinstance(value, (float, int)):
            raise TypeError(
                f"@value must be integer or float value, but {value} was applied.")
        self._value = value
        self._enabled = True
    def __bool__(self):
        return self._enabled
    @property
    def value(self):
        """
        float: value of the unit
        """
        return self._value
    def enable(self):
        """
        Enable the unit.
        """
        self._enabled = True
    def disable(self):
        """
        Disable the unit.
        """
        self._enabled = False
ʻUnit has a unique value and state (bool`: True / False).
Creating a Unit:
iter_advanced.py
unit1 = Unit(value=1.0)
#Display value if Unit status is True
if unit1:
    print(unit1.value)
# 1.0
State to False:
iter_advanced.py
# Disable
unit1.disable()
#Display value if Unit status is True
if unit1:
    print(unit1.value)
#No output
State to True:
iter_advanced.py
# Disable
unit1.enable()
#Display value if Unit status is True
if unit1:
    print(unit1.value)
# 1.0
Create a class Series that stores the information of multiple ʻUnit`s and iterates.
iter_advanced.py
class Series(object):
    """
    A series of units.
    """
    def __init__(self):
        self._units = []
    def __iter__(self):
        yield from self._units
    def add(self, unit):
        """
        Append a unit.
        Args:
            unit (Unit]): the smallest unit
        Raises:
            TypeError: unit is not an instance of Unit
        """
        if not isinstance(unit, Unit):
            raise TypeError("@unit must be a instance of Unit")
        self._units.append(unit)
    def _validate_index(self, num):
        """
        Validate the index number.
        Args:
            num (int): index number of a unit
        Raises:
            TypeError: @num is not an integer
            IndexError: @num is not a valid index number
        """
        if not isinstance(num, int):
            raise TypeError(
                f"@num must be integer, but {num} was applied.")
        try:
            self._units[num]
        except IndexError:
            raise IndexError(f"@num must be under {len(self._units)}")
    def enable(self, num):
        """
        Enable a unit.
        Args:
            num (int): index of the unit to be enabled
        Raises:
            TypeError: @num is not an integer
            IndexError: @num is not a valid index number
        """
        self._validate_index(num)
        self._units[num].enable()
    def disable(self, num):
        """
        Disable a unit.
        Args:
            num (int): index of the unit to be disabled
        Raises:
            TypeError: @num is not an integer
            IndexError: @num is not a valid index number
        """
        self._validate_index(num)
        self._units[num].disable()
To check the behavior of the Series class, create a function that returns the value of the Unit whose state is True.
iter_advanced.py
def show_enabled(series):
    """
    Show the values of enabled units.
    """
    if not isinstance(series, Series):
        raise TypeError("@unit must be a instance of Series")
    print([unit.value for unit in series if unit])
Register ʻUnit with Series and display all ʻUnit values.
iter_advanced.py
# Create a series of units
series = Series()
[series.add(Unit(i)) for i in range(6)]
show_enabled(series)
# [0, 1, 2, 3, 4, 5]
If the state of the 5th and 6th Units (values are 4 and 5) is set to False ...
iter_advanced.py
# Disable two units
series.disable(4)
series.disable(5)
show_enabled(series)
# [0, 1, 2, 3]
When the state of the 5th Unit (value is 4) is returned to True ...
iter_advanced.py
# Enable one disabled unit
series.enable(4)
show_enabled(series)
# [0, 1, 2, 3, 4]
Thank you for browsing. Thank you for your hard work.
How did you create this article: I used this mechanism in my own Python package CovsirPhy: COVID-19 analysis with phase-dependent SIRs. -PhaseUnit: Period during which the parameter values of the ODE model are constant -PhaseSeries: Infected Number Scenario
Recommended Posts