listless
⚓︎
Listless = generators, iterators, and async iterators, Oh My!
Functions:
-
aiterable
–Convert any-iterable to an async iterator
-
chunk
–Yield chunks of size n from an iterable/sequence/collection
-
chunks
–Yield chunks of something slice-able with length <= chunk_size
-
chunkseq
–Yield chunks of size n from a Sequence
-
chunkstr
–Yield chunks of size n from a string
-
enumerate_async
–Enumerate (async) over any iterable
-
exhaust
–Exhaust an interable; useful for evaluating a map object.
-
filter_is_none
–Filter values that
is None
; checkout filter_none for false-y filtering -
filter_none
–Filter
None
values from an iterable -
flatten
–Flatten possibly nested iterables of sequences to a flat list
-
flatten_seq
–Flatten possibly nested iterables of sequences to a flat list
-
flatten_strings
–Flatten possibly nested iterables of sequences to a list of strings
-
is_sequence
–Check if an object is a sequence
-
it_product
–Product of all the elements in an iterable of numbers
-
itlen
–Return the length/num-items in an iterable
-
list_async
–Consume any iterable (async/sync) and return as a list
-
next_async
–Return the next item of any iterator/iterable (sync or async
-
nyield
–Yield the first n items of an iterable
-
pairs
–Yield pairs of adjacent elements
-
partition
–Partition an iterable into chunks of size n
-
set_async
–Consume any iterable (async/sync) and return as a list
-
spliterable
–1 generator + True/False-function => 2 generators (True-gen, False-gen)
-
unique
–Alias for unique_gen
-
unique_gen
–Yield unique values (ordered) from an iterable
-
xmap
–Apply a function to each element of an iterable immediately
-
zip_async
–Async verstion of builtin zip function
aiterable
⚓︎
aiterable(
it: Union[Iterable[_T], AsyncIterable[_T]],
) -> AsyncIterator[_T]
Convert any-iterable to an async iterator
Examples:
>>> from asyncio import run
>>> plain_jane_list = list(range(10))
>>> async def consume_aiterable(it):
... stuff = []
... async for el in aiterable(it):
... stuff.append(el)
... return stuff
>>> run(consume_aiterable(plain_jane_list))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> async def async_gen():
... for b in range(10):
... yield b
>>> run(consume_aiterable(async_gen()))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> class AsyncIterable:
... def __aiter__(self):
... return async_gen()
>>> run(consume_aiterable(AsyncIterable()))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
chunk
⚓︎
chunk(
it: Union[Sequence[_T], Collection[_T]], n: int
) -> Iterable[Union[Sequence[_T], Collection[_T], str]]
Yield chunks of size n from an iterable/sequence/collection
Examples:
>>> list(chunk([1, 2, 3, 4, 5, 6], 3))
[[1, 2, 3], [4, 5, 6]]
chunks
⚓︎
chunks(
it: Collection[_T], chunk_size: int
) -> Iterable[Collection[_T]]
chunks(
it: Union[Sequence[_T], Collection[_T]], chunk_size: int
) -> Iterable[Union[Sequence[_T], Collection[_T], str]]
Yield chunks of something slice-able with length <= chunk_size
Parameters:
Returns:
-
Iterable[Union[Sequence[_T], Collection[_T], str]]
–Iterable of the chunks
Examples:
>>> list(chunks([1, 2, 3, 4, 5, 6], 3))
[[1, 2, 3], [4, 5, 6]]
>>> list(chunks([1, 2, 3, 4, 5, 6], 2))
[[1, 2], [3, 4], [5, 6]]
>>> list(chunks('abcdefghijklmnopqrstuvwxyz', 13))
['abcdefghijklm', 'nopqrstuvwxyz']
Can chunk where it length is not divisible by chunk_size
>>> list(chunks('abcdefghijklmnopqrstuvwxyz', 4))
['abcd', 'efgh', 'ijkl', 'mnop', 'qrst', 'uvwx', 'yz']
>>> list(chunks((el for el in range(10)), 4))
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9)]
chunkseq
⚓︎
Yield chunks of size n from a Sequence
enumerate_async
async
⚓︎
enumerate_async(
it: AnyIterable[_T], start: int = 0
) -> AsyncIterator[Tuple[int, _T]]
Enumerate (async) over any iterable
Examples:
>>> async def t():
... return [item async for item in enumerate_async('abcde')]
>>> from asyncio import run as aiorun
>>> aiorun(t())
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]
exhaust
⚓︎
Exhaust an interable; useful for evaluating a map object.
Parameters:
-
it
⚓︎Iterable[_T]
) –Iterable to exhaust
-
maxlen
⚓︎Optional[int]
, default:0
) –Maximum length of the deque; if 0, deque is unbounded
Examples:
>>> a = [1, 2, 3, 4, 5, 6]
>>> a_map = map(lambda x: x*2, a)
>>> a_exhausted = exhaust(a_map)
>>> a = [1, 2, 3, 4, 5, 6]
>>> b = []
>>> def square_and_append_to_b(n):
... b.append(n**2)
>>> a_map = map(square_and_append_to_b, a)
>>> a_exhausted = exhaust(a_map)
>>> a_exhausted
deque([], maxlen=0)
>>> b
[1, 4, 9, 16, 25, 36]
>>> another_map = map(lambda x: x*2, a)
>>> another_exhausted = exhaust(another_map, maxlen=2)
>>> another_exhausted
deque([10, 12], maxlen=2)
filter_is_none
⚓︎
Filter values that is None
; checkout filter_none for false-y filtering
Parameters:
Returns:
-
Iterable[_T]
–filter object with None values excluded
Examples:
>>> list(filter_is_none([1, 2, None, 3, 4, None, 5, "a_string???"]))
[1, 2, 3, 4, 5, 'a_string???']
>>> list(filter_is_none([-1, 0, 1, '', 's', None, [], ['s'], {}]))
[-1, 0, 1, '', 's', [], ['s'], {}]
filter_none
⚓︎
Filter None
values from an iterable
Parameters:
Returns:
-
Iterable[_T]
–filter object with None values excluded
Examples:
>>> list(filter_none([1, 2, None, 3, 4, None, 5, "a_string???"]))
[1, 2, 3, 4, 5, 'a_string???']
>>> list(filter_none([1, 2, '', 3, 4, None, 5, "a_string???", []]))
[1, 2, 3, 4, 5, 'a_string???']
>>> list(filter_none([-1, 0, 1, '', 's', None, [], ['s'], {}]))
[-1, 1, 's', ['s']]
This function is p simple and importing it and calling it might actually
be more characters to type than just using filter(None, ya_iterable)
but it is a fire thing to know and you can totally show off with this,
also outperforms the list/gen comprehension equivalent, by quite a bit::
import random
res = [random.randrange(1, 300, 1) for i in range(1000)]
lists = [
res
]
for i in range(40):
random.shuffle(res)
lists.append(res)
def filter_one(it):
return (i for i in it if i is not None)
def filter_two(it):
return filter(None, it)
Timing the first function (generator comprehension)::
%%timeit
for i in range(100):
for l in lists:
a = list(filter_one(l))
180 ms +/- 184 µs/loop (mean +/- std. dev. of 7 runs, 10 loops each)
Timing the second function (filter)::
%%timeit
for i in range(100):
for l in lists:
a = list(filter_two(l))
42.5 ms +/- 112 µs/loop (mean +/- std. dev. of 7 runs, 10 loops each)
flatten
⚓︎
Flatten possibly nested iterables of sequences to a flat list
Examples:
>>> list(flatten("cmd", ["uno", "dos", "tres"]))
['cmd', 'uno', 'dos', 'tres']
>>> list(flatten("cmd", ["uno", "dos", "tres", ["4444", "five"]]))
['cmd', 'uno', 'dos', 'tres', '4444', 'five']
>>> list(flatten("cmd", ["uno", "dos", "tres", ["4444", "five", 123]]))
['cmd', 'uno', 'dos', 'tres', '4444', 'five', 123]
flatten_seq
⚓︎
Flatten possibly nested iterables of sequences to a flat list
Examples:
>>> list(flatten_seq("cmd", ["uno", "dos", "tres"]))
['cmd', 'uno', 'dos', 'tres']
>>> list(flatten_seq("cmd", ["uno", "dos", "tres", ["4444", "five"]]))
['cmd', 'uno', 'dos', 'tres', '4444', 'five']
flatten_strings
⚓︎
Flatten possibly nested iterables of sequences to a list of strings
Examples:
>>> from listless import flatten_strings
>>> list(flatten_strings("cmd", ["uno", "dos", "tres"]))
['cmd', 'uno', 'dos', 'tres']
>>> list(flatten_strings("cmd", ["uno", "dos", "tres", ["4444", "five", 123]]))
['cmd', 'uno', 'dos', 'tres', '4444', 'five', '123']
is_sequence
⚓︎
Check if an object is a sequence
Examples:
>>> is_sequence([1, 2, 3])
True
>>> is_sequence('abc')
False
>>> is_sequence(1)
False
>>> is_sequence(None)
False
>>> is_sequence(True)
False
>>> is_sequence((1, 2, 3))
True
is_subscriptable
⚓︎
Check if an object is subscriptable
Examples:
>>> is_subscriptable([1, 2, 3])
True
>>> is_subscriptable('abc')
True
>>> is_subscriptable(1)
False
>>> is_subscriptable(None)
False
>>> is_subscriptable(True)
False
>>> is_subscriptable((1, 2, 3))
True
it_product
⚓︎
Product of all the elements in an iterable of numbers
Parameters:
Returns:
Examples:
>>> it_product([1, 2, 3, 4])
24
>>> it_product(tuple([1, 2, 3, 4]))
24
>>> it_product([-1, -2, -3, 4])
-24
>>> it_product([-1, -2, 3, 4, 0.5])
12.0
itlen
⚓︎
Return the length/num-items in an iterable
This consumes the iterable.
Parameters:
Returns:
-
int
–Length of an iterable
Examples:
>>> itlen(range(10))
10
>>> itlen(x for x in range(1000000) if x % 3 == 0)
333334
>>> l = [x for x in range(1000000) if x % 3 == 0]
>>> itlen(l + l, unique=True)
333334
list_async
async
⚓︎
list_async(itr: AnyIterable[_T]) -> List[_T]
Consume any iterable (async/sync) and return as a list
Examples:
>>> async def t():
... return await list_async(range(5))
>>> from asyncio import run as aiorun
>>> aiorun(t())
[0, 1, 2, 3, 4]
next_async
async
⚓︎
next_async(it: AnyIterator[_T]) -> _T
Return the next item of any iterator/iterable (sync or async
Examples:
>>> from asyncio import run as aiorun
>>> async def async_gen():
... for b in range(10):
... yield b
>>> gen = async_gen()
>>> async def fn(gen):
... first = await next_async(gen)
... second = await next_async(gen)
... return first, second
>>> aiorun(fn(gen))
(0, 1)
>>> aiorun(fn(iter(range(2))))
(0, 1)
nyield
⚓︎
Yield the first n items of an iterable
Examples:
>>> list(nyield([1, 2, 3, 4, 5, 6], 3))
[1, 2, 3]
pairs
⚓︎
Yield pairs of adjacent elements
Examples:
>>> list(pairs([1, 2, 3, 4]))
[(1, 2), (2, 3), (3, 4)]
>>> list(pairs(['a', 'b', 'c']))
[('a', 'b'), ('b', 'c')]
>>> list(pairs('abc'))
[('a', 'b'), ('b', 'c')]
partition
⚓︎
partition(
it: Sequence[_T],
n: int,
*,
pad: bool = False,
padval: Any = None,
) -> Iterable[Sequence[_T]]
Partition an iterable into chunks of size n
Parameters:
-
it
⚓︎Sequence[_T]
) –Iterable to partition
-
n
⚓︎int
) –Size of the partition chunks
-
pad
⚓︎bool
, default:False
) –Pad parts with padval if True, else do not pad
-
padval
⚓︎Any
, default:None
) –Value to pad with
Returns:
Examples:
>>> list(partition([1, 2, 3, 4, 5, 6], 3))
[(1, 2, 3), (4, 5, 6)]
>>> list(partition([1, 2, 3, 4, 5, 6], 2))
[(1, 2), (3, 4), (5, 6)]
>>> for part in partition('abcdefghijklmnopqrstuvwxyz', 13):
... print(part)
('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm')
('n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z')
>>> for part in partition('abcdefghijklmnopqrstuvwxyz', 4):
... print(part)
('a', 'b', 'c', 'd')
('e', 'f', 'g', 'h')
('i', 'j', 'k', 'l')
('m', 'n', 'o', 'p')
('q', 'r', 's', 't')
('u', 'v', 'w', 'x')
>>> for part in partition('abcdefghijklmnopqrstuvwxyz', 4, pad=True):
... print(part)
('a', 'b', 'c', 'd')
('e', 'f', 'g', 'h')
('i', 'j', 'k', 'l')
('m', 'n', 'o', 'p')
('q', 'r', 's', 't')
('u', 'v', 'w', 'x')
('y', 'z', None, None)
>>> for part in partition('abcdefghijklmnopqrstuvwxyz', 4, pad=True, padval=...):
... print(part)
('a', 'b', 'c', 'd')
('e', 'f', 'g', 'h')
('i', 'j', 'k', 'l')
('m', 'n', 'o', 'p')
('q', 'r', 's', 't')
('u', 'v', 'w', 'x')
('y', 'z', Ellipsis, Ellipsis)
Raises:
-
TypeError
–If
n
is not and int -
ValueError
–if
n
is less than 1
set_async
async
⚓︎
set_async(itr: AnyIterable[_T]) -> Set[_T]
Consume any iterable (async/sync) and return as a list
Examples:
>>> async def t():
... return await set_async(range(5))
>>> from asyncio import run as aiorun
>>> aiorun(t())
{0, 1, 2, 3, 4}
spliterable
⚓︎
1 generator + True/False-function => 2 generators (True-gen, False-gen)
Parameters:
-
it
⚓︎Iterable[_T]
) –iterable to split
-
fn
⚓︎Callable[[_T], bool]
) –Function to evaluate iterable elements and returns True or False
Returns:
Examples:
>>> is_even = lambda n: n % 2 == 0
>>> a, b = spliterable(range(10), is_even)
>>> list(a)
[0, 2, 4, 6, 8]
>>> list(b)
[1, 3, 5, 7, 9]
unique
⚓︎
Alias for unique_gen
Examples:
>>> l = [*range(10), *range(10)]
>>> list(unique(l))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> tuple(unique(['cat', 'mouse', 'dog', 'hen'], key=len))
('cat', 'mouse')
unique_gen
⚓︎
Yield unique values (ordered) from an iterable
Parameters:
-
it
⚓︎Iterable[_T]
) –Iterable
-
key
⚓︎Optional[Callable[[_T], _K]]
, default:None
) –Optional callable to use to get the key to use for uniqueness
Returns:
-
Iterable[_T]
–Generator that yields unique values as they appear
Examples:
>>> l = [*range(10), *range(10)]
>>> list(unique_gen(l))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> tuple(unique_gen(['cat', 'mouse', 'dog', 'hen'], key=len))
('cat', 'mouse')
xmap
⚓︎
Apply a function to each element of an iterable immediately
Parameters:
-
func
⚓︎Callable[[_T], _R]
) –Function to apply to each element
-
it
⚓︎Iterable[_T]
) –iterable to apply func to
-
maxlen
⚓︎Optional[int]
, default:0
) –Maximum length of the deque; if 0, deque is unbounded
Returns:
-
Deque[_R]
–Deque of the possible results if maxlen is greater than 0
Examples:
>>> xmap(lambda x: x*2, list(range(1, 7)))
deque([], maxlen=0)
>>> xmap(lambda x: x*2, list(range(1, 7)), maxlen=2)
deque([10, 12], maxlen=2)
>>> xmap(lambda x: x*2, list(range(1, 7)), maxlen=None)
deque([2, 4, 6, 8, 10, 12])
zip_async
async
⚓︎
zip_async(
*iterables: AnyIterable[Any],
) -> AsyncIterator[Tuple[Any, ...]]
Async verstion of builtin zip function
Example
from asyncio import run as aiorun from listless import zip_async from listless import list_async, iter_async # for fake async iters a, b, c = iter_async(range(4)), iter_async(range(6)), iter_async(range(5)) aiorun(list_async(zip_async(a, b, c))) [(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3)]