Skip to content

Functional style wrapper for iterators and iterables in python

Notifications You must be signed in to change notification settings

chriscz/funciter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Functional style iterator and iterable maninulation for python.

Description

Sequence instances can be backed by either iterators or iterables.

When an iterable object is used, the Sequence will be reusable. However if an iterator is used, then the sequence is depletable and can only be used once.

Each operation on the sequence returns a new Sequence instance that does not affect the original (this is not the case when an iterator was passed during construction).

To make the distinction between iterables and iterators clear, consider the following definitions:

  • IteraTORs are objects that hold the state of an iteration over an iterable.
  • IteraBLEs are objects that can be iterated over.

Here's an example:

A = [1, -12, 2, -99, 3] # A is an iteraBLE

d = (for _ in A if _ > 0) # d is an iteraTOR

for i in d:
    print i
# out: 1
#      2
#      3    
# d is now depleted and cannot be reused    

Usage examples (usage.py)

from __future__ import print_function
from funciter import Sequence
from funciter import IteratorAlreadyStarted

# since these are `iterables` and NOT iterators,
# we can reuse our constructer iterator
A = range(11)
B = "cats like catnip"

# ------------------------------------------------------------------------------ 
# 1. use it like a normal iterator
# ------------------------------------------------------------------------------ 
for i in Sequence(range(5)):
    print(i)

# ------------------------------------------------------------------------------ 
# 2. Filter certain elements
# ------------------------------------------------------------------------------ 
cats_only = Sequence(B).filter(lambda x: x in 'cat ')
print(''.join(cats_only))

# it's reusable because it's backed by an iteraBLE
print("it works again! again!")
for x in cats_only:
    print(x)

# ------------------------------------------------------------------------------ 
# 3. Skip the even elements
# ------------------------------------------------------------------------------ 
Ai = Sequence(A)[1::2]
for i in Ai:
    print(i)

# again, since it's backed by a iterable, it's reusable    
for i in Ai:
    print(i)

# ------------------------------------------------------------------------------ 
# 4. Apply another operation to the sequence
# ------------------------------------------------------------------------------ 
print("skip the first odd element (1)")
Qi = Ai[1:]

for i in Qi:
    print(i)

# ------------------------------------------------------------------------------ 
# 5. what about a depletable source?
# ------------------------------------------------------------------------------ 

# x is now a sequence backed by an iteraTOR    
x = Sequence(_ for _ in range(10)) 

# Either s or e can be used, but not both, since
# they are backed by the same iteraTOR
s = x[::2] 
e = x[::3]

# Consume the first element of `s` 
for i in s:
    print(i)
    break

# Let's try using `e`
try:
    for i in e:
        print(i)
except IteratorAlreadyStarted as e:
    print(e)
    print("yup, started iterators can't be reused!")
else:
    raise RuntimeError('Expected an exception to be raised, but none was!')

# ------------------------------------------------------------------------------ 
# 6. Another iteraTOR example
# ------------------------------------------------------------------------------ 
x = Sequence(_ for _ in range(10))
e = x[::3]

ei = iter(e)

print("HEAD", e.head())

# Cannot start again!
try:
    x[::2].filter(lambda x: x > 0)[0]
except IteratorAlreadyStarted as e:
    print(e)
else:
    raise RuntimeError('Expected an exception to be raised, but none was!')

try:
    for i in ei:
        print(i)
except IteratorAlreadyStarted as e:
    print(e)
else:
    raise RuntimeError('Expected an exception to be raised, but none was!')

Known Issues

  • No caching of iterated values, therefore the whole chain of transformation is applied every time the iterator is restarted.

About

Functional style wrapper for iterators and iterables in python

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages