steptools

steptools.range is as a drop-in replacement for range, adding with support for anything that behaves reasonably like a number, including floats, datetime.datetime, datetime.date, fractions.Fraction, and decimal.Decimal.

if stop is None, start will be used as stop and start will be “zero” created with type(start)().

Similar to range, generated items are EXCLUSIVE of stop. inclusive=True allows stop to be returned, assuming it’s a multiple of step.

type(step)() should construct a “zero” version of step which has no result if added to start. If this does not work, set zero_step explicitly.

Examples

steptools.range can act as a drop-in replacement for range to iterate over ints. range is probably faster!

>>> list(range(3))
[0, 1, 2]
>>> list(range(3, inclusive=True))
[0, 1, 2, 3]
>>> list(range(5, -3, -2))
[5, 3, 1, -1]
>>> list(range(1.1, 1.5, .2))
[1.1, 1.3]

steptools.range can iterate over over datetime.datetime and datetime.date objects using a datetime.timedelta as the step.

>>> from datetime import datetime, timedelta
>>> list(range(datetime(2000, 1, 1), datetime(1999,12,30),
...     timedelta(days=-1)))
[datetime.datetime(2000, 1, 1, 0, 0), datetime.datetime(1999, 12, 31, 0, 0)]

steptools.range can iterate over over fractions.Fraction

>>> from fractions import Fraction
>>> list(range(Fraction(1,3), Fraction(2,3), Fraction(1,6)))
[Fraction(1, 3), Fraction(1, 2)]
>>> list(range(Fraction(1,3), Fraction(5,3)))
[Fraction(1, 3), Fraction(4, 3)]

steptools.range can iterate over over decimal.Decimal

>>> from decimal import Decimal
>>> list(range(Decimal("1.33"), Decimal("1.66"), Decimal("0.11")))
[Decimal('1.33'), Decimal('1.44'), Decimal('1.55')]

Requirements

steptools does not depend on any non-core libraries, however the values you pass in for start, stop, step, and zero_step must have certain behavior. The following should all work reasonably without raising exception. The comparisons (< and ==) should return a bool.:

if zero_step is None:
    type(step)() # must be logically "zero"

step == zero_step

if step < zero_step:
    stop < start
    stop < (start+step)
else:
    start < stop
    (start+step) < stop
if inclusive:
    start == stop
    (start+step) == stop
if stop is none:
    type(start)() # must be logically "zero"