The EzSequence library for Python can easily generate custom iterable sequences of numbers for you.
Go to file
2025-02-02 21:03:12 -07:00
ezsequence Initial commit. 2025-02-02 21:03:12 -07:00
tests Initial commit. 2025-02-02 21:03:12 -07:00
.editorconfig Initial commit. 2025-02-02 21:03:12 -07:00
.gitignore Initial commit. 2025-02-02 21:03:12 -07:00
LICENSE.txt Initial commit. 2025-02-02 21:03:12 -07:00
pyproject.toml Initial commit. 2025-02-02 21:03:12 -07:00
README.md Initial commit. 2025-02-02 21:03:12 -07:00
runtests.py Initial commit. 2025-02-02 21:03:12 -07:00

EzSequence

A quick and dirty sequence generator for Python.

About

  • EzSequence uses a fluent interface to strive for a self-documenting code approach. It works like the Builder pattern.
  • EzSequence produces iterators that you can use in your FOR loops, your list comprehensions or to be converted to a list all at once.
  • EzSequence supports infinite sequences but has safety built in.

Basic Usage

from ezsequence import *

seq = sequence_from(1).to(5)
print(seq)

Full Usage

Static Methods

There is only one!

sequence_from()

This function starts your sequence with an initial value and some default behavior. From there, you can customize it with any of the fluent methods listed below.

Fluent Methods

Method Description Example Output
.by(increment: int) Optional. Specifies a gap between values. Default is 1. Cannot be used with .with_increment(). sequence_from(1) .to(5) .by(2) [1, 3, 5]
.with_increment(increment_func: Callable[[int, int], int]) Optional. Specifies the translation of the value on each iteration. By default, value is incremented by 1. sequence .from(1) .to(5) .with_increment(lambda index, value: value + 2) [1, 3, 5]
.until(condition: Callable[[int, int], int]) Optional. Specifies a terminating condition in the sequence. Value is inclusive. In other words, once the condition is met, the next iteration will end the sequence. Multiple .until() calls are permitted. Otherwise, it is the same as .to(). sequence_from(1) .until(lambda index, value: value == 5) [1, 2, 3, 4, 5]
.as_long_as(condition: Callable[[int, int], int]) Optional. Specifies a terminating condition in the sequence. Value is exclusive. In other words, once the condition is violated, the current iteration will end the sequence. Multiple .as_long_as() calls are permitted. sequence_from(1) .as_long_as(lambda index, value: value <= 5) [1, 2, 3, 4, 5]
.when(condition: Callable[[int, int], int]) Optional. Specifies a condition for including this value in the sequence output. By default, all values are included. Multiple .when() calls are supported. Evluations do not terminate the sequence. sequence_from(1) .to(5) .when(lambda index, value: value % 2 == 1) [1, 3, 5]
.not_when(condition: Callable[[int, int], int]) Optional. Specifies a condition for excluding values, overriding any .when() condition that included them. By default, there are no exclusions. Multiple .not_when() calls are supported. Evaluations do not terminate the sequence. sequence_from(1) .to(5) .not_when(lambda index, value: value % 2 == 0) [1, 3, 5]
.safely(max_attempts: int) Optional. Prevents unintended infinite loops caused by .when() and .not_when() filters which will hang in the while loop that searches for the next unfiltered vlaue in the sequence. Takes an optional parameter for the max number of attempts to find an unfiltered value. Default max attempts is 10000. sequence_from(1) .to(5) .when(lambda index, value: value < 0) .safely(100) Exception: Safe iteration limit exceeded. 100 attempts exceeded.
.unsafely() Optional. Allows unintended infinite loops caused by .when() and .not_when() filters which may hang in the _while_loop that searchs for the next unfiltered value in the sequence. sequence_from(1) .with_increment(lambda index, value: random.Random() .randint(1, 1000)) .as_long_as(lambda index, value: value != 50) .unsafely() (No exception but may hang indefinitely)
.translate_as(projection: Callable[[int, int], any]) Optional. Specifies a function to convert each element that is returned by the iterator. sequence_from(5) .to(9) .translate_as(lambda index, value: value - 4) [1, 2, 3, 4, 5]
.reset() Rewinds the sequence to the beginning so it can be reused. seq.reset() N/A

More Examples

Odds

seq = sequence_from(1).to(5) \
	.when(lambda index, value: value % 2 == 1)

Evens

seq = sequence_from(1).to(5) \
	.when(lambda index, value: value % 2 == 0)

Backward

seq = sequence_from(1) \
	.by(-1) \
	.until(lambda index, value: value == -5)

Random Bytes

import random
rnd = random.Random()
seq = sequence_from(1).to(8) \
	.translate_as(lambda index, value: rnd.randint(0, 255))
data = list(seq)

Random AlphaNumeric Text

import random
rnd = random.Random()
seq = sequence_from(1).to(8) \
	.translate_as(lambda index, value: chr(rnd.randint(1, 26) + 64))
data = "".join(seq)

Reset Example

import random
rnd = random.Random()
seq = sequence_from(1).to(8) \
	.translate_as(lambda index, value: chr(rnd.randint(1, 26) + 64))

data1 = "".join(seq)
seq.reset()
data2 = "".join(seq)

Random CSV values

import random
rnd = random.Random()

field_count = 5
row_count = 3

def create_field_data(data_seq):
	data_seq.reset()
	return "".join(data_seq)

def create_row(field_seq):
	field_seq.reset()
	return ",".join(field_seq)

data_seq = sequence_from(1).to(8) \
	.translate_as(lambda index, value: chr(rnd.randint(1, 26) + 64))

field_seq = sequence_from(1).to(field_count) \
	.translate_as(lambda index, value: "\"" + create_field_data(data_seq) + "\"")

row_seq = sequence_from(1).to(row_count)

for row in row_seq:
	print(create_row(field_seq))

Guid

import random
rnd = random.Random()
section_generators = \
[
	sequence_from(1).to(8).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
	sequence_from(1).to(4).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
	sequence_from(1).to(4).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
	sequence_from(1).to(4).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3]),
	sequence_from(1).to(12).translate_as(lambda index, value: hex(rnd.randint(0, 15))[2:3])
]

sections = ["".join(generator) for generator in section_generators]
guid = "-".join(sections)
print(guid)

Factorials

from functools import reduce

factorial_of = 5
factors = sequence_from(factorial_of) \
	.with_increment(lambda index, value: value - 1) \
	.until(lambda index, value: value == 1)
factorial = reduce(lambda running, value: running * value, factors)

print(list(factors.reset()))
print(factorial)

Fibonacci

import itertools

class Fibonacci:
	def __init__(self):
		self.previous2 = 0
		self.previous1 = 1
	def calculate_fibonacci(self, value):
		next_value = self.previous1 + self.previous2
		self.previous2 = self.previous1
		self.previous1 = next_value
		return next_value
	def sequence(self):
		seq = sequence_from(1).to(20) \
			.translate_as(lambda index, value: self.calculate_fibonacci(value)) \
			.as_long_as(lambda index, value: value < 500000000)
		return itertools.chain([1], seq)

fib = list(Fibonacci().sequence())
print(fib)

Multiple when, not_when, until and as_long_as

seq = sequence_from(1).to(50) \
	.by(3) \
	.when(lambda index, value: value >= 10) \
	.when(lambda index, value: value % 2 == 1) \
	.when_not(lambda index, value: value == 25) \
	.as_long_as(lambda index, value: value <= 40) \
	.as_long_as(lambda index, value: value != 38) \
	.safely(10)

print(list(seq))