pythoninheritanceintegeroverridingabc

How to override Integer in Python?


I want to inherit from integers and only redefine some methods.

The goal is to have this behaviour:

>>> i = Iter()
>>> i == 0
True
>>> next(i)
Iter<1>
>>> next(i)
Iter<2>
>>> i + 10
12

The naive approach would be to inherit from int:

class Iter(int):
    def __new__(cls, start=0, increment=1):
        return super().__new__(cls, start)
    
    def __init__(self, start=0, increment=1):
        self.increment = increment

    def __repr__(self):
        return f"Iter<{int(self)}>"

    def __next__(self):
        self += self.increment # DO NOT WORK
        return self.value

Unfortunately, int is immutable. I tried to use the ABC for integers, but I don't really want to redefine all operators:

from numbers import Integral
class Iter(Integral):
   ...
i = Iter()
TypeError: Can't instantiate abstract class I with abstract methods
__abs__, __add__, __and__, __ceil__, __eq__, __floor__,
__floordiv__, __int__, __invert__, __le__, __lshift__, __lt__, 
__mod__, __mul__, __neg__, __or__, __pos__, __pow__, __radd__, 
__rand__, __rfloordiv__, __rlshift__, __rmod__, __rmul__, __ror__, 
__round__, __rpow__, __rrshift__, __rshift__, __rtruediv__, 
__rxor__, __truediv__, __trunc__, __xor__

Any other ideas?


Solution

  • Are you aware that itertools.count exists? It does most of what you are trying to do, except for being able to use the instance itself as an integer.

    It is not possible to extend either int or itertools.count for this purpose.

    Regarding the methods for operators, the reason you would need to define them is because there is not an obvious return value in this case, even if extending int or count worked. What would __add__ return - an integer, or another instance of Iter with its value incremented by the given amount? You would need to decide that, and implement them for your use case.

    Rather than extending any existing builtin class, it may be easier to define your own. You can define the __int__ method to control what happens when int(...) is called on an instance of your class.

    Example implementation:

    class Iter:
        def __init__(self, start: int = 0, increment: int = 1):
            self._value = start
            self._increment = increment
    
        def __int__(self) -> int:
            return self._value
    
        def __repr__(self) -> str:
            return f'Iter<{self._value}>'
    
        def __next__(self) -> int:
            self._value += self._increment
            return self._value
    

    Example use:

    >>> i = Iter()
    
    >>> next(i)
    1
    
    >>> next(i)
    2
    
    >>> i
    Iter<2>
    
    >>> int(i)
    2