Skip to content

API Reference

Preface

The usage of the library usually follows the following pattern:

  1. Create a loop using one of the factory functions (which return a Loop instance).
  2. Apply one or more modifier methods (in a method chaining style) to customize the looping behaviour.
  3. Finally, consume the loop, for example using a for statement.

Factory Functions

loop.loop_over(iterable)

Construct a new Loop that iterates over iterable.

Customize the looping behaviour by chaining different Loop methods and finally use a for statement like you normally would.

Example
from loop import loop_over


for word in loop_over(['Hello', 'World', '!']):
    print(word)
Hello
World
!
PARAMETER DESCRIPTION
iterable

The object to be looped over.

TYPE: Iterable[S]

RETURNS DESCRIPTION
Loop[S, S, FALSE, FALSE, TRUE]

Returns a new Loop instance wrapping iterable.

loop.loop_range(*args)

Shorthand for loop_over(range(*args)).

Modifier Methods

loop.Loop.map(function, *args, **kwargs)

Apply function to each item in iterable by calling function(item, *args, **kwargs).

Example
from loop import loop_over


for word in loop_over(['Hello', 'World', '!']).map(lambda s: s.upper()):
    print(word)
HELLO
WORLD
!
PARAMETER DESCRIPTION
function

Function to be applied on each item in the loop.

TYPE: Callable[[T], L]

args

Passed as *args (after the loop variable) to each call to function.

DEFAULT: ()

kwargs

Passed as **kwargs to each call to function.

DEFAULT: {}

Note

By default, applying map(function, *args, **kwargs) is not the same as applying map(functools.partial(function, *args, **kwargs)) because functools.partial would pass *args BEFORE the loop item.

loop.Loop.filter(predicate, *args, **kwargs)

Skip items in iterable for which predicate(item, *args, **kwargs) is false.

Example
from loop import loop_over


for number in loop_over(range(10)).filter(lambda x: x%2==0):
    print(number)
0
2
4
6
8
PARAMETER DESCRIPTION
predicate

Function that accepts the loop variable and returns a boolean.

TYPE: Callable[[T], bool]

args

Passed as *args (after the loop variable) to each call to predicate.

DEFAULT: ()

kwargs

Passed as **kwargs to each call to predicate.

DEFAULT: {}

Note

If show_progress() was enabled with a known total, each time an item is skipped, the progress bar's total will be reduced by one.

loop.Loop.next_call_with(unpacking=None, args_first=False)

Change how arguments are passed to function in map() (or predicate in filter()).

Arguments are explained using the following table:

unpacking args_first Resulting Call
None False func(x, *args, **kwargs) (this is the default behaviour)
None True func(*args, x, **kwargs)
"*" False func(*x, *args, **kwargs)
"*" True func(*args, *x, **kwargs)
"**" Any func(*args, **x, **kwargs)

Note

Each invocation of next_call_with() applies only to the next map()/filter(), subsequent calls will resume to default behaviour.

loop.Loop.returning(enumerations=False, inputs=False, outputs=True)

Set the return type of this loop.

By default, only outputs are returned.

The order of returned items is (enumerations, inputs, outputs).

Example
from loop import loop_over


for retval in loop_over(range(0, 10, 2)).map(pow, 2).returning(enumerations=True, inputs=True, outputs=True):
    print(retval)
(0, 0, 0)
(1, 2, 4)
(2, 4, 16)
(3, 6, 36)
(4, 8, 64)
PARAMETER DESCRIPTION
enumerations

If True, return value will include the (zero-based) index of the current iteration.

TYPE: bool DEFAULT: False

inputs

If True, return value will include the raw value from the underlying iterable, before any map() has been applied.

TYPE: bool DEFAULT: False

outputs

If True, return value will include the output of the last map() operation.

TYPE: bool DEFAULT: True

loop.Loop.show_progress(refresh=False, postfix_str=None, total=None, **kwargs)

Display a tqdm.tqdm progress bar as the iterable is being consumed.

Example

import time

from loop import loop_over


seconds = [1.1, 4.5, 0.9, 5.8]
loop_over(seconds).map(time.sleep).show_progress(desc='Sleeping', total=len).exhaust()
Sleeping: 100%|█████████████████████████████████████████| 4/4 [00:12<00:00,  3.08s/it]

PARAMETER DESCRIPTION
refresh

If True, tqdm.refresh() will be called after every iteration, this makes the progress bar more responsive but reduces the iteration rate.

TYPE: bool DEFAULT: False

postfix_str

Used for calling tqdm.set_postfix_str(). If a string, it will be set only once in the beginning. If a callable, it accepts the loop variable, returns a postfix (which can be of any type) on top of which str() is applied.

TYPE: Optional[Union[str, Callable[[Any], Any]]] DEFAULT: None

total

Same as in tqdm.__init__(), but can also be a callable that accepts an iterable and returns an int, which is used as the new total.

TYPE: Optional[Union[int, Callable[[Iterable], int]]] DEFAULT: None

kwargs

Forwarded to tqdm.__init__() as-is.

DEFAULT: {}

Note

When postfix_str is callable, it always takes a single parameter, the value of which depends on what was set in returning().

For example:

(loop_over(...).
 returning(enumerations=True, inputs=True, outputs=True).
 show_progress(postfix_str=lambda x: f'idx={x[0]},inp={x[1]},out={x[2]}'))

Here x is a tuple containing the current index, input and output.

loop.Loop.concurrently(how, exceptions='raise', chunksize=None, num_workers=None)

Apply the functions and predicates from all map() and filter() calls concurrently.

The order of the outputs is preserved. Each item in iterable gets its own worker.

Example

import os
import time
from loop import loop_over


def show_pid(i):
    time.sleep(0.1)
    print(f'{i} on process {os.getpid()}')


loop_over(range(10)).map(show_pid).concurrently('processes', num_workers=5).exhaust()
0 on process 17336
1 on process 17320
2 on process 13960
3 on process 5136
4 on process 17224
5 on process 17336
7 on process 13960
6 on process 17320
8 on process 5136
9 on process 17224

PARAMETER DESCRIPTION
how

If "threads", uses ThreadPool.

If "processes", uses ProcessPool (from the pathos library).

TYPE: Literal['threads', 'processes']

exceptions

If "raise", exceptions are not caught and the first exception in one of the calls will be immediately raised.

If "return", exceptions are caught and returned instead of their corresponding outputs.

TYPE: Literal['raise', 'return'] DEFAULT: 'raise'

chunksize

Passed to imap() method of ProcessPool / ThreadPool.

This is used to consume (and concurrently process) up to chunksize items at a time, which can solve memory issues in "heavy" iterables.

TYPE: Optional[int] DEFAULT: None

num_workers

Number of workers to be used in the process/thread pool. If None, will be set automatically. If 0, disables concurrency entirely.

TYPE: Optional[int] DEFAULT: None

Consumer Methods

loop.Loop.__iter__()

The most common way to consume a loop is to simply iterate over it.

Example
from loop import loop_over


items = ...

for item in loop_over(items):
    # Do something with item
    pass

loop.Loop.exhaust()

Consume the loop without returning any results.

This maybe useful when you map functions only for their side effects.

Example

from loop import loop_over


items = []
loop_over(range(5)).map(items.append).exhaust()
print(items)
[0, 1, 2, 3, 4]

loop.Loop.reduce(function, initializer=_missing)

Consume the loop and reduce it to a single value using function.

function and (the optional) initializer have the same meaning as in functools.reduce().

Example

from loop import loop_over


vec = [-1.1, 25.3, 4.9]
sum_squares = loop_over(vec).map(lambda x: x**2).reduce(lambda x,y: x+y)
print(f'The L2 norm of {vec} equals {sum_squares**0.5:.2f}')
The L2 norm of [-1.1, 25.3, 4.9] equals 25.79