Module xtuples.xtuples
Expand source code
from __future__ import annotations
# ---------------------------------------------------------------
import abc
import typing
import dataclasses
import collections
import operator
import itertools
import functools
# ---------------------------------------------------------------
# NOTE: at worst, not worse, than an un-optimised canonical solution
# where I cribbed from the itertools recipes (and other python docs), all credit to the original authors.
# where i didn't, i probably should have.
# ---------------------------------------------------------------
def pipe(f, obj, *args, at = None, discard=False, **kwargs):
if at is None:
res = f(obj, *args, **kwargs)
elif isinstance(at, int):
res = f(*args[:at], obj, *args[at:], **kwargs)
elif isinstance(at, str):
res = f(*args, **{at: obj}, **kwargs)
else:
assert False, at
if not discard:
return res
return obj
# ---------------------------------------------------------------
# TODO: some kind of validation placeholder?
# called in init, eg. quarter in [1 .. 4]
class nTuple(abc.ABC):
@abc.abstractmethod
def __abstract__(self):
# NOTE: here to prevent initialise instances of this
# but rather use the decorator and typing.NamedTuple
return
@staticmethod
def pipe(obj, f, *args, at = None, **kwargs):
"""
>>> _Example = _Example(1, "a")
>>> _Example.pipe(lambda a, b: a, None)
_Example(x=1, s='a', it=iTuple())
>>> _Example.pipe(lambda a, b: a, None, at = 1)
>>> _Example.pipe(lambda a, b: a, None, at = 'b')
>>> _Example.pipe(lambda a, b: a, a=None, at = 'b')
>>> _Example.pipe(lambda a, b: a, b=None, at = 'a')
_Example(x=1, s='a', it=iTuple())
>>> _Example.pipe(lambda a, b: a, None, at = 0)
_Example(x=1, s='a', it=iTuple())
"""
return pipe(f, obj, *args, at = at, **kwargs)
@staticmethod
def partial(obj, f, *args, **kwargs):
return functools.partial(f, obj, *args, **kwargs)
@classmethod
def is_subclass(cls, t):
"""
>>> nTuple.is_subclass(tuple)
False
>>> nTuple.is_subclass(_Example(1, "a"))
False
>>> nTuple.is_subclass(_Example)
True
"""
try:
is_sub = issubclass(t, tuple)
except:
is_sub = False
return (
is_sub and
hasattr(t, "cls") and
hasattr(t, "pipe") and
hasattr(t, "partial")
)
@classmethod
def is_instance(cls, obj):
"""
>>> nTuple.is_instance(tuple)
False
>>> nTuple.is_instance(_Example)
False
>>> nTuple.is_instance(_Example(1, "a"))
True
"""
return (
cls.is_subclass(type(obj)) and
hasattr(obj, '_asdict') and
hasattr(obj, '_fields')
)
@staticmethod
def annotations(obj):
"""
>>> ex = _Example(1, "a")
>>> ex.pipe(ex.cls.annotations)
{'x': ForwardRef('int'), 's': ForwardRef('str'), 'it': ForwardRef('iTuple')}
"""
return fDict(obj.__annotations__)
@classmethod
def as_dict(cls, obj):
"""
>>> ex = _Example(1, "a")
>>> ex.pipe(ex.cls.as_dict)
{'x': 1, 's': 'a', 'it': iTuple()}
"""
return fDict(obj._asdict())
@classmethod
def decorate(meta, **methods):
def decorator(cls):
cls.pipe = meta.pipe
cls.partial = meta.partial
cls.cls = meta
for k, f in methods.items():
setattr(cls, k, f)
return cls
return decorator
@classmethod
def enum(meta, cls):
cls = meta.decorate()(cls)
return functools.lru_cache(maxsize=1)(cls)
# ---------------------------------------------------------------
class fDict(collections.UserDict):
__slots__ = ()
data: dict
def pipe(self, f, *args, at=None, **kwargs):
"""
>>> fDict({0: 1}).pipe(lambda d: d.map_values(
... lambda v: v + 1
... ))
{0: 2}
"""
res = pipe(f, self, *args, at = at, **kwargs)
if isinstance(res, dict):
return fDict(res)
return res
def partial(self, f, *args, **kwargs):
"""
>>> f = fDict({0: 1}).partial(
... lambda d, n: d.map_values(lambda v: v + n)
... )
>>> f(1)
{0: 2}
>>> f(2)
{0: 3}
"""
return functools.partial(f, self, *args, **kwargs)
def keys_tuple(self):
"""
>>> fDict({0: 1}).keys_tuple()
iTuple(0)
"""
return iTuple.from_keys(self)
def values_tuple(self):
"""
>>> fDict({0: 1}).values_tuple()
iTuple(1)
"""
return iTuple.from_values(self)
def items_tuple(self):
"""
>>> fDict({0: 1}).items_tuple()
iTuple((0, 1))
"""
return iTuple.from_items(self)
# NOTE: we have separate map implementations
# as they are constant size, dict to dict
# other iterator functions should use iTuple (from the above)
def map_keys(self, f, *args, **kwargs):
"""
>>> fDict({0: 1}).map_keys(lambda v: v + 1)
{1: 1}
"""
return fDict(dict(
(f(k, *args, **kwargs), v) for k, v in self.items()
))
def map_values(self, f, *args, **kwargs):
"""
>>> fDict({0: 1}).map_values(lambda v: v + 1)
{0: 2}
"""
return fDict(dict(
(k, f(v, *args, **kwargs)) for k, v in self.items()
))
def map_items(self, f, *args, **kwargs):
"""
>>> fDict({0: 1}).map_items(lambda k, v: (v, k))
{1: 0}
"""
return fDict(dict(
f(k, v, *args, **kwargs) for k, v in self.items()
))
def invert(self):
"""
>>> fDict({0: 1}).invert()
{1: 0}
"""
return fDict(dict((v, k) for k, v in self.items()))
# ---------------------------------------------------------------
tuple_getitem = tuple.__getitem__
class iTuple(tuple):
# -----
@staticmethod
def __new__(cls, *args):
if len(args) == 1:
v = args[0]
if isinstance(v, cls):
return v
elif not isinstance(v, collections.abc.Iterable):
return ().__new__(cls, (v,))
return super().__new__(cls, *args)
def __repr__(self):
"""
>>> iTuple()
iTuple()
>>> iTuple(iTuple((3, 2,)))
iTuple(3, 2)
"""
s = tuple.__repr__(self)
return "{}({})".format(
type(self).__name__,
s[1:-2 if s[-2] == "," else -1],
)
def __str__(self):
return self.__repr__()
def __hash__(self):
return hash(self)
# -----
@classmethod
def empty(cls):
return cls(tuple())
@classmethod
def one(cls, v):
return cls((v,))
@classmethod
def none(cls, n):
"""
>>> iTuple.none(3)
iTuple(None, None, None)
"""
return cls((None for _ in range(n)))
@classmethod
def range(cls, *args, **kwargs):
"""
>>> iTuple.range(3)
iTuple(0, 1, 2)
"""
return cls(range(*args, **kwargs))
@classmethod
def from_keys(cls, d):
"""
>>> iTuple.from_keys({i: i + 1 for i in range(2)})
iTuple(0, 1)
"""
return cls(d.keys())
@classmethod
def from_values(cls, d):
"""
>>> iTuple.from_values({i: i + 1 for i in range(2)})
iTuple(1, 2)
"""
return cls(d.values())
@classmethod
def from_items(cls, d):
"""
>>> iTuple.from_items({i: i + 1 for i in range(2)})
iTuple((0, 1), (1, 2))
"""
return cls(d.items())
@classmethod
def from_index(cls, s):
return cls(s.index)
@classmethod
def from_index_values(cls, s):
return cls(s.index.values)
@classmethod
def from_columns(cls, s):
return cls(s.columns)
# -----
def pipe(self, f, *args, at = None, **kwargs):
"""
>>> iTuple.range(2).pipe(lambda it: it)
iTuple(0, 1)
>>> iTuple.range(2).pipe(
... lambda it, v: it.map(lambda x: x * v), 2
... )
iTuple(0, 2)
"""
return pipe(f, self, *args, at = at, **kwargs)
def partial(self, f, *args, **kwargs):
"""
>>> f = iTuple.range(2).partial(
... lambda it, v: it.map(lambda x: x * v)
... )
>>> f(2)
iTuple(0, 2)
>>> f(3)
iTuple(0, 3)
"""
return functools.partial(f, self, *args, **kwargs)
# -----
# def __len__(self):
# return len(self)
# def __contains__(self, v):
# return v in self
def len(self):
"""
>>> iTuple.range(3).len()
3
"""
return len(self)
def len_range(self):
"""
>>> iTuple.range(3).len()
3
"""
return type(self).range(self.len())
def append(self, value, *values):
"""
>>> iTuple().append(1)
iTuple(1)
>>> iTuple.range(1).append(1)
iTuple(0, 1)
>>> iTuple.range(1).append(1, 2)
iTuple(0, 1, 2)
>>> iTuple.range(1).append(1, 2, 3)
iTuple(0, 1, 2, 3)
>>> iTuple.range(1).append(1, (2,))
iTuple(0, 1, (2,))
"""
return iTuple((*self, value, *values,))
def prepend(self, value, *values):
"""
>>> iTuple().prepend(1)
iTuple(1)
>>> iTuple.range(1).prepend(1)
iTuple(1, 0)
>>> iTuple.range(1).prepend(1, 2)
iTuple(1, 2, 0)
>>> iTuple.range(1).prepend(1, 2, 3)
iTuple(1, 2, 3, 0)
>>> iTuple.range(1).prepend(1, (2,))
iTuple(1, (2,), 0)
"""
return iTuple((value, *values, *self,))
def zip(self, *itrs, lazy = False, at = None):
"""
>>> iTuple([[1, 1], [2, 2], [3, 3]]).zip()
iTuple((1, 2, 3), (1, 2, 3))
>>> iTuple([iTuple.range(3), iTuple.range(1, 4)]).zip()
iTuple((0, 1), (1, 2), (2, 3))
>>> iTuple.range(3).zip(iTuple.range(1, 4))
iTuple((0, 1), (1, 2), (2, 3))
"""
if len(itrs) == 0:
res = zip(*self)
elif at is None:
res = zip(self, *itrs)
elif isinstance(at, int):
res = zip(*itrs[:at], self, *itrs[at:])
else:
assert False, at
return res if lazy else iTuple(res)
def flatten(self):
"""
>>> iTuple.range(3).map(lambda x: [x]).flatten()
iTuple(0, 1, 2)
"""
return iTuple(itertools.chain(*self))
def extend(self, value, *values):
"""
>>> iTuple.range(1).extend((1,))
iTuple(0, 1)
>>> iTuple.range(1).extend([1])
iTuple(0, 1)
>>> iTuple.range(1).extend([1], [2])
iTuple(0, 1, 2)
>>> iTuple.range(1).extend([1], [[2]])
iTuple(0, 1, [2])
>>> iTuple.range(1).extend([1], [[2]], [2])
iTuple(0, 1, [2], 2)
"""
return iTuple(itertools.chain.from_iterable(
(self, value, *values)
))
def pretend(self, value, *values):
"""
>>> iTuple.range(1).pretend((1,))
iTuple(1, 0)
>>> iTuple.range(1).pretend([1])
iTuple(1, 0)
>>> iTuple.range(1).pretend([1], [2])
iTuple(1, 2, 0)
>>> iTuple.range(1).pretend([1], [[2]])
iTuple(1, [2], 0)
>>> iTuple.range(1).pretend([1], [[2]], [2])
iTuple(1, [2], 2, 0)
"""
return iTuple(itertools.chain.from_iterable(
(value, *values, self)
))
def any(self, f):
return any(self.map(f, lazy=True))
def all(self, f):
return all(self.map(f, lazy=True))
def filter_eq(self, v, f = None, eq = None, lazy = False):
"""
>>> iTuple.range(3).filter_eq(1)
iTuple(1)
"""
if f is None and eq is None:
res = filter(lambda x: x == v, self)
elif f is not None:
res = filter(lambda x: f(x) == v, self)
elif eq is not None:
res = filter(lambda x: eq(x, v), self)
elif f is not None and eq is not None:
res = filter(lambda x: eq(f(x), v), self)
else:
assert False
return res if lazy else type(self)(res)
def filter(self, f, eq = None, lazy = False, **kws):
"""
>>> iTuple.range(3).filter(lambda x: x > 1)
iTuple(2)
"""
# res = []
# for v in self.iter():
# if f(v):
# res.append(v)
return type(self)((
v for v in self.iter() if f(v, **kws)
))
# return self.filter_eq(True, f = f, eq = eq, lazy = lazy)
def filterstar(self, f, eq = None, lazy = False, **kws):
"""
>>> iTuple.range(3).filter(lambda x: x > 1)
iTuple(2)
"""
# res = []
# for v in self.iter():
# if f(v):
# res.append(v)
return type(self)((
v for v in self.iter() if f(*v, **kws)
))
def is_none(self):
return self.filter(lambda v: v is None)
def not_none(self):
return self.filter(lambda v: v is not None)
def map(
self,
f,
*iterables,
at = None,
lazy = False
) -> iTuple:
"""
>>> iTuple.range(3).map(lambda x: x * 2)
iTuple(0, 2, 4)
"""
# if lazy and at is None:
# return map(f, self, *iterables)
if at is None:
return iTuple(map(f, self, *iterables))
elif isinstance(at, int):
return iTuple(map(
f, *iterables[:at], self, *iterables[at:]
))
elif isinstance(at, str):
return iTuple(map(
f, *iterables, **{at: self}
))
else:
assert False, at
# args, kwargs
def mapstar(self, f):
return iTuple(itertools.starmap(f, self))
def get(self, i):
if isinstance(i, slice):
return type(self)(self[i])
return self[i]
def __getitem__(self, i):
if isinstance(i, slice):
return type(self)(tuple_getitem(self, i))
return tuple_getitem(self, i)
# def __iter__(self):
# return iter(self)
def iter(self):
"""
>>> for x in iTuple.range(3).iter(): print(x)
0
1
2
"""
return iter(self)
def enumerate(self):
"""
>>> iTuple.range(3).enumerate()
iTuple((0, 0), (1, 1), (2, 2))
"""
# TODO: allow lazy
return iTuple(enumerate(self))
def chunkby(
self,
f,
lazy = False,
keys = False,
pipe= None,
):
"""
>>> iTuple.range(3).chunkby(lambda x: x < 2)
iTuple(iTuple(0, 1), iTuple(2))
>>> iTuple.range(3).chunkby(
... lambda x: x < 2, keys=True, pipe=fDict
... )
{True: iTuple(0, 1), False: iTuple(2)}
"""
# TODO: lazy no keys
res = itertools.groupby(self, key=f)
if lazy and keys and pipe is None:
return res
if pipe is None:
pipe = iTuple
if keys:
return pipe((k, iTuple(g),) for k, g in res)
else:
return pipe(iTuple(g) for k, g in res)
def groupby(
self, f, lazy = False, keys = False, pipe = None
):
"""
>>> iTuple.range(3).groupby(lambda x: x < 2)
iTuple(iTuple(2), iTuple(0, 1))
>>> iTuple.range(3).groupby(
... lambda x: x < 2, keys=True, pipe=fDict
... )
{False: iTuple(2), True: iTuple(0, 1)}
"""
res = (
self.chunkby(f, keys=True)
.sort(f = lambda kg: kg[0])
.chunkby(lambda kg: kg[0], keys = True)
.map(lambda k_kgs: (
k_kgs[0],
k_kgs[1].mapstar(lambda k, g: g).flatten(),
))
)
if pipe is None:
pipe = iTuple
if keys:
return pipe((k, iTuple(g),) for k, g in res)
else:
return pipe(iTuple(g) for k, g in res)
def first(self):
"""
>>> iTuple.range(3).first()
0
"""
return self[0]
def last(self):
"""
>>> iTuple.range(3).last()
2
"""
return self[-1]
def pop_first(self):
return self[1:]
def pop_last(self):
return self[:-1]
def first_where(self, f, default = None):
"""
>>> iTuple.range(3).first_where(lambda v: v > 0)
1
"""
for v in self:
if f(v):
return v
return default
def last_where(self, f, default = None):
"""
>>> iTuple.range(3).last_where(lambda v: v < 2)
1
"""
for v in reversed(self):
if f(v):
return v
return default
def take(self, n):
"""
>>> iTuple.range(3).take(2)
iTuple(0, 1)
"""
return self[:n]
def tail(self, n):
"""
>>> iTuple.range(3).tail(2)
iTuple(1, 2)
"""
return self[-n:]
def reverse(self, lazy = False):
"""
>>> iTuple.range(3).reverse()
iTuple(2, 1, 0)
"""
if lazy:
return reversed(self)
return type(self)(reversed(self))
def take_while(self, f, n = None, lazy = False):
"""
>>> iTuple.range(3).take_while(lambda v: v < 1)
iTuple(0)
"""
def iter():
i = 0
for v in self:
if f(v) and (n is None or i < n):
yield v
i += 1
else:
return
res = iter()
return res if lazy else type(self)(res)
def tail_while(self, f, n = None):
"""
>>> iTuple.range(3).tail_while(lambda v: v > 1)
iTuple(2)
"""
i = 0
for v in reversed(self):
if f(v) and (n is None or i < n):
i += 1
else:
break
return self.tail(i)
# NOTE: from as in, starting from first true
# versus above, which is until first false
def take_after(self, f, n = None, lazy = False):
"""
>>> iTuple.range(3).take_after(lambda v: v < 1)
iTuple(1, 2)
>>> iTuple.range(3).take_after(lambda v: v < 1, n = 1)
iTuple(1)
"""
def iter():
i = 0
for v in self:
if f(v):
pass
elif n is None or i < n:
yield v
i += 1
else:
return
res = iter()
return res if lazy else type(self)(res)
def tail_after(self, f, n = None):
"""
>>> iTuple.range(3).tail_after(lambda v: v < 2)
iTuple(0, 1)
>>> iTuple.range(3).tail_after(lambda v: v < 2, 1)
iTuple(1)
"""
l = 0
r = 0
for v in reversed(self):
if not f(v):
l += 1
elif n is None or r < n:
r += 1
else:
break
return self.tail(l + r).take(r)
def islice(self, left = None, right = None):
"""
>>> iTuple.range(5).islice(1, 3)
iTuple(1, 2)
"""
return self[left:right]
def unique(self):
"""
>>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique()
iTuple(1, 3, 2, 4)
"""
def iter():
seen: typing.Set = set()
seen_add = seen.add
seen_contains = seen.__contains__
for v in itertools.filterfalse(seen_contains, self):
seen_add(v)
yield v
return type(self)(iter())
def sort(self, f = lambda v: v):
"""
>>> iTuple.range(3).reverse().sort()
iTuple(0, 1, 2)
>>> iTuple.range(3).sort()
iTuple(0, 1, 2)
"""
return type(self)(sorted(self, key = f))
def accumulate(self, f, initial = None, lazy = False):
"""
>>> iTuple.range(3).accumulate(lambda acc, v: v)
iTuple(0, 1, 2)
>>> iTuple.range(3).accumulate(lambda acc, v: v, initial=0)
iTuple(0, 0, 1, 2)
>>> iTuple.range(3).accumulate(operator.add)
iTuple(0, 1, 3)
"""
if lazy:
return itertools.accumulate(self, func=f, initial=initial)
return iTuple(itertools.accumulate(
self, func=f, initial=initial
))
def foldcum(self, *args, initial=None, **kwargs):
"""
>>> iTuple.range(3).foldcum(lambda acc, v: v)
iTuple(0, 1, 2)
>>> iTuple.range(3).foldcum(operator.add)
iTuple(0, 1, 3)
"""
# res = []
# acc = initial
# for x in self.iter():
# acc = f(acc, x)
# res.append(acc)
# return iTuple(tuple(res))
return self.accumulate(*args, **kwargs)
def fold(self, f, initial=None):
"""
>>> iTuple.range(3).fold(lambda acc, v: v)
2
>>> iTuple.range(3).fold(lambda acc, v: v, initial=0)
2
>>> iTuple.range(3).fold(operator.add)
3
"""
# acc = initial
# for v in self.iter():
# acc = f(acc, v)
# return iTuple(tuple(acc))
if initial is not None:
return functools.reduce(f, self, initial)
else:
return functools.reduce(f, self)
def foldstar(self, f, initial=None):
"""
>>> iTuple.range(3).fold(lambda acc, v: v)
2
>>> iTuple.range(3).fold(lambda acc, v: v, initial=0)
2
>>> iTuple.range(3).fold(operator.add)
3
"""
# acc = initial
# for v in self.iter():
# acc = f(acc, v)
# return iTuple(tuple(acc))
fstar = lambda acc, v: f(acc, *v)
if initial is not None:
return functools.reduce(fstar, self, initial)
else:
return functools.reduce(fstar, self)
# -----
# combinatorics
# -----
ituple = iTuple
# ---------------------------------------------------------------
@nTuple.decorate()
class _Example(typing.NamedTuple):
"""
>>> ex = _Example(1, "a")
>>> ex
_Example(x=1, s='a', it=iTuple())
>>> ex.cls
<class 'xtuples.xtuples.nTuple'>
>>> ex.pipe(lambda nt: nt.x)
1
>>> f = ex.partial(lambda nt, v: nt.x * v)
>>> f(2)
2
>>> f(3)
3
"""
# NOTE: cls, pipe, partial are mandatory boilerplate
x: int
s: str
it: iTuple = iTuple([])
@property
def cls(self):
...
def pipe(self, f, *args, at = None, **kwargs):
...
def partial(self, f, *args, at = None, **kwargs):
...
# ---------------------------------------------------------------
# TODO: context manager to control
# if we add the type information when writing to json or not
# TODO: context mananger to control
# lazy default behaviour (ie. default to lazy or not)
__all__ = [
"iTuple",
"nTuple",
"fDict",
# "_Example",
]
# ---------------------------------------------------------------
Classes
class fDict (dict=None, /, **kwargs)
-
Expand source code
class fDict(collections.UserDict): __slots__ = () data: dict def pipe(self, f, *args, at=None, **kwargs): """ >>> fDict({0: 1}).pipe(lambda d: d.map_values( ... lambda v: v + 1 ... )) {0: 2} """ res = pipe(f, self, *args, at = at, **kwargs) if isinstance(res, dict): return fDict(res) return res def partial(self, f, *args, **kwargs): """ >>> f = fDict({0: 1}).partial( ... lambda d, n: d.map_values(lambda v: v + n) ... ) >>> f(1) {0: 2} >>> f(2) {0: 3} """ return functools.partial(f, self, *args, **kwargs) def keys_tuple(self): """ >>> fDict({0: 1}).keys_tuple() iTuple(0) """ return iTuple.from_keys(self) def values_tuple(self): """ >>> fDict({0: 1}).values_tuple() iTuple(1) """ return iTuple.from_values(self) def items_tuple(self): """ >>> fDict({0: 1}).items_tuple() iTuple((0, 1)) """ return iTuple.from_items(self) # NOTE: we have separate map implementations # as they are constant size, dict to dict # other iterator functions should use iTuple (from the above) def map_keys(self, f, *args, **kwargs): """ >>> fDict({0: 1}).map_keys(lambda v: v + 1) {1: 1} """ return fDict(dict( (f(k, *args, **kwargs), v) for k, v in self.items() )) def map_values(self, f, *args, **kwargs): """ >>> fDict({0: 1}).map_values(lambda v: v + 1) {0: 2} """ return fDict(dict( (k, f(v, *args, **kwargs)) for k, v in self.items() )) def map_items(self, f, *args, **kwargs): """ >>> fDict({0: 1}).map_items(lambda k, v: (v, k)) {1: 0} """ return fDict(dict( f(k, v, *args, **kwargs) for k, v in self.items() )) def invert(self): """ >>> fDict({0: 1}).invert() {1: 0} """ return fDict(dict((v, k) for k, v in self.items()))
Ancestors
- collections.UserDict
- collections.abc.MutableMapping
- collections.abc.Mapping
- collections.abc.Collection
- collections.abc.Sized
- collections.abc.Iterable
- collections.abc.Container
Class variables
var data : dict
Methods
def invert(self)
-
>>> fDict({0: 1}).invert() {1: 0}
Expand source code
def invert(self): """ >>> fDict({0: 1}).invert() {1: 0} """ return fDict(dict((v, k) for k, v in self.items()))
def items_tuple(self)
-
>>> fDict({0: 1}).items_tuple() iTuple((0, 1))
Expand source code
def items_tuple(self): """ >>> fDict({0: 1}).items_tuple() iTuple((0, 1)) """ return iTuple.from_items(self)
def keys_tuple(self)
-
>>> fDict({0: 1}).keys_tuple() iTuple(0)
Expand source code
def keys_tuple(self): """ >>> fDict({0: 1}).keys_tuple() iTuple(0) """ return iTuple.from_keys(self)
def map_items(self, f, *args, **kwargs)
-
>>> fDict({0: 1}).map_items(lambda k, v: (v, k)) {1: 0}
Expand source code
def map_items(self, f, *args, **kwargs): """ >>> fDict({0: 1}).map_items(lambda k, v: (v, k)) {1: 0} """ return fDict(dict( f(k, v, *args, **kwargs) for k, v in self.items() ))
def map_keys(self, f, *args, **kwargs)
-
>>> fDict({0: 1}).map_keys(lambda v: v + 1) {1: 1}
Expand source code
def map_keys(self, f, *args, **kwargs): """ >>> fDict({0: 1}).map_keys(lambda v: v + 1) {1: 1} """ return fDict(dict( (f(k, *args, **kwargs), v) for k, v in self.items() ))
def map_values(self, f, *args, **kwargs)
-
>>> fDict({0: 1}).map_values(lambda v: v + 1) {0: 2}
Expand source code
def map_values(self, f, *args, **kwargs): """ >>> fDict({0: 1}).map_values(lambda v: v + 1) {0: 2} """ return fDict(dict( (k, f(v, *args, **kwargs)) for k, v in self.items() ))
def partial(self, f, *args, **kwargs)
-
>>> f = fDict({0: 1}).partial( ... lambda d, n: d.map_values(lambda v: v + n) ... ) >>> f(1) {0: 2} >>> f(2) {0: 3}
Expand source code
def partial(self, f, *args, **kwargs): """ >>> f = fDict({0: 1}).partial( ... lambda d, n: d.map_values(lambda v: v + n) ... ) >>> f(1) {0: 2} >>> f(2) {0: 3} """ return functools.partial(f, self, *args, **kwargs)
def pipe(self, f, *args, at=None, **kwargs)
-
>>> fDict({0: 1}).pipe(lambda d: d.map_values( ... lambda v: v + 1 ... )) {0: 2}
Expand source code
def pipe(self, f, *args, at=None, **kwargs): """ >>> fDict({0: 1}).pipe(lambda d: d.map_values( ... lambda v: v + 1 ... )) {0: 2} """ res = pipe(f, self, *args, at = at, **kwargs) if isinstance(res, dict): return fDict(res) return res
def values_tuple(self)
-
>>> fDict({0: 1}).values_tuple() iTuple(1)
Expand source code
def values_tuple(self): """ >>> fDict({0: 1}).values_tuple() iTuple(1) """ return iTuple.from_values(self)
class iTuple (*args)
-
Built-in immutable sequence.
If no argument is given, the constructor returns an empty tuple. If iterable is specified the tuple is initialized from iterable's items.
If the argument is a tuple, the return value is the same object.
Expand source code
class iTuple(tuple): # ----- @staticmethod def __new__(cls, *args): if len(args) == 1: v = args[0] if isinstance(v, cls): return v elif not isinstance(v, collections.abc.Iterable): return ().__new__(cls, (v,)) return super().__new__(cls, *args) def __repr__(self): """ >>> iTuple() iTuple() >>> iTuple(iTuple((3, 2,))) iTuple(3, 2) """ s = tuple.__repr__(self) return "{}({})".format( type(self).__name__, s[1:-2 if s[-2] == "," else -1], ) def __str__(self): return self.__repr__() def __hash__(self): return hash(self) # ----- @classmethod def empty(cls): return cls(tuple()) @classmethod def one(cls, v): return cls((v,)) @classmethod def none(cls, n): """ >>> iTuple.none(3) iTuple(None, None, None) """ return cls((None for _ in range(n))) @classmethod def range(cls, *args, **kwargs): """ >>> iTuple.range(3) iTuple(0, 1, 2) """ return cls(range(*args, **kwargs)) @classmethod def from_keys(cls, d): """ >>> iTuple.from_keys({i: i + 1 for i in range(2)}) iTuple(0, 1) """ return cls(d.keys()) @classmethod def from_values(cls, d): """ >>> iTuple.from_values({i: i + 1 for i in range(2)}) iTuple(1, 2) """ return cls(d.values()) @classmethod def from_items(cls, d): """ >>> iTuple.from_items({i: i + 1 for i in range(2)}) iTuple((0, 1), (1, 2)) """ return cls(d.items()) @classmethod def from_index(cls, s): return cls(s.index) @classmethod def from_index_values(cls, s): return cls(s.index.values) @classmethod def from_columns(cls, s): return cls(s.columns) # ----- def pipe(self, f, *args, at = None, **kwargs): """ >>> iTuple.range(2).pipe(lambda it: it) iTuple(0, 1) >>> iTuple.range(2).pipe( ... lambda it, v: it.map(lambda x: x * v), 2 ... ) iTuple(0, 2) """ return pipe(f, self, *args, at = at, **kwargs) def partial(self, f, *args, **kwargs): """ >>> f = iTuple.range(2).partial( ... lambda it, v: it.map(lambda x: x * v) ... ) >>> f(2) iTuple(0, 2) >>> f(3) iTuple(0, 3) """ return functools.partial(f, self, *args, **kwargs) # ----- # def __len__(self): # return len(self) # def __contains__(self, v): # return v in self def len(self): """ >>> iTuple.range(3).len() 3 """ return len(self) def len_range(self): """ >>> iTuple.range(3).len() 3 """ return type(self).range(self.len()) def append(self, value, *values): """ >>> iTuple().append(1) iTuple(1) >>> iTuple.range(1).append(1) iTuple(0, 1) >>> iTuple.range(1).append(1, 2) iTuple(0, 1, 2) >>> iTuple.range(1).append(1, 2, 3) iTuple(0, 1, 2, 3) >>> iTuple.range(1).append(1, (2,)) iTuple(0, 1, (2,)) """ return iTuple((*self, value, *values,)) def prepend(self, value, *values): """ >>> iTuple().prepend(1) iTuple(1) >>> iTuple.range(1).prepend(1) iTuple(1, 0) >>> iTuple.range(1).prepend(1, 2) iTuple(1, 2, 0) >>> iTuple.range(1).prepend(1, 2, 3) iTuple(1, 2, 3, 0) >>> iTuple.range(1).prepend(1, (2,)) iTuple(1, (2,), 0) """ return iTuple((value, *values, *self,)) def zip(self, *itrs, lazy = False, at = None): """ >>> iTuple([[1, 1], [2, 2], [3, 3]]).zip() iTuple((1, 2, 3), (1, 2, 3)) >>> iTuple([iTuple.range(3), iTuple.range(1, 4)]).zip() iTuple((0, 1), (1, 2), (2, 3)) >>> iTuple.range(3).zip(iTuple.range(1, 4)) iTuple((0, 1), (1, 2), (2, 3)) """ if len(itrs) == 0: res = zip(*self) elif at is None: res = zip(self, *itrs) elif isinstance(at, int): res = zip(*itrs[:at], self, *itrs[at:]) else: assert False, at return res if lazy else iTuple(res) def flatten(self): """ >>> iTuple.range(3).map(lambda x: [x]).flatten() iTuple(0, 1, 2) """ return iTuple(itertools.chain(*self)) def extend(self, value, *values): """ >>> iTuple.range(1).extend((1,)) iTuple(0, 1) >>> iTuple.range(1).extend([1]) iTuple(0, 1) >>> iTuple.range(1).extend([1], [2]) iTuple(0, 1, 2) >>> iTuple.range(1).extend([1], [[2]]) iTuple(0, 1, [2]) >>> iTuple.range(1).extend([1], [[2]], [2]) iTuple(0, 1, [2], 2) """ return iTuple(itertools.chain.from_iterable( (self, value, *values) )) def pretend(self, value, *values): """ >>> iTuple.range(1).pretend((1,)) iTuple(1, 0) >>> iTuple.range(1).pretend([1]) iTuple(1, 0) >>> iTuple.range(1).pretend([1], [2]) iTuple(1, 2, 0) >>> iTuple.range(1).pretend([1], [[2]]) iTuple(1, [2], 0) >>> iTuple.range(1).pretend([1], [[2]], [2]) iTuple(1, [2], 2, 0) """ return iTuple(itertools.chain.from_iterable( (value, *values, self) )) def any(self, f): return any(self.map(f, lazy=True)) def all(self, f): return all(self.map(f, lazy=True)) def filter_eq(self, v, f = None, eq = None, lazy = False): """ >>> iTuple.range(3).filter_eq(1) iTuple(1) """ if f is None and eq is None: res = filter(lambda x: x == v, self) elif f is not None: res = filter(lambda x: f(x) == v, self) elif eq is not None: res = filter(lambda x: eq(x, v), self) elif f is not None and eq is not None: res = filter(lambda x: eq(f(x), v), self) else: assert False return res if lazy else type(self)(res) def filter(self, f, eq = None, lazy = False, **kws): """ >>> iTuple.range(3).filter(lambda x: x > 1) iTuple(2) """ # res = [] # for v in self.iter(): # if f(v): # res.append(v) return type(self)(( v for v in self.iter() if f(v, **kws) )) # return self.filter_eq(True, f = f, eq = eq, lazy = lazy) def filterstar(self, f, eq = None, lazy = False, **kws): """ >>> iTuple.range(3).filter(lambda x: x > 1) iTuple(2) """ # res = [] # for v in self.iter(): # if f(v): # res.append(v) return type(self)(( v for v in self.iter() if f(*v, **kws) )) def is_none(self): return self.filter(lambda v: v is None) def not_none(self): return self.filter(lambda v: v is not None) def map( self, f, *iterables, at = None, lazy = False ) -> iTuple: """ >>> iTuple.range(3).map(lambda x: x * 2) iTuple(0, 2, 4) """ # if lazy and at is None: # return map(f, self, *iterables) if at is None: return iTuple(map(f, self, *iterables)) elif isinstance(at, int): return iTuple(map( f, *iterables[:at], self, *iterables[at:] )) elif isinstance(at, str): return iTuple(map( f, *iterables, **{at: self} )) else: assert False, at # args, kwargs def mapstar(self, f): return iTuple(itertools.starmap(f, self)) def get(self, i): if isinstance(i, slice): return type(self)(self[i]) return self[i] def __getitem__(self, i): if isinstance(i, slice): return type(self)(tuple_getitem(self, i)) return tuple_getitem(self, i) # def __iter__(self): # return iter(self) def iter(self): """ >>> for x in iTuple.range(3).iter(): print(x) 0 1 2 """ return iter(self) def enumerate(self): """ >>> iTuple.range(3).enumerate() iTuple((0, 0), (1, 1), (2, 2)) """ # TODO: allow lazy return iTuple(enumerate(self)) def chunkby( self, f, lazy = False, keys = False, pipe= None, ): """ >>> iTuple.range(3).chunkby(lambda x: x < 2) iTuple(iTuple(0, 1), iTuple(2)) >>> iTuple.range(3).chunkby( ... lambda x: x < 2, keys=True, pipe=fDict ... ) {True: iTuple(0, 1), False: iTuple(2)} """ # TODO: lazy no keys res = itertools.groupby(self, key=f) if lazy and keys and pipe is None: return res if pipe is None: pipe = iTuple if keys: return pipe((k, iTuple(g),) for k, g in res) else: return pipe(iTuple(g) for k, g in res) def groupby( self, f, lazy = False, keys = False, pipe = None ): """ >>> iTuple.range(3).groupby(lambda x: x < 2) iTuple(iTuple(2), iTuple(0, 1)) >>> iTuple.range(3).groupby( ... lambda x: x < 2, keys=True, pipe=fDict ... ) {False: iTuple(2), True: iTuple(0, 1)} """ res = ( self.chunkby(f, keys=True) .sort(f = lambda kg: kg[0]) .chunkby(lambda kg: kg[0], keys = True) .map(lambda k_kgs: ( k_kgs[0], k_kgs[1].mapstar(lambda k, g: g).flatten(), )) ) if pipe is None: pipe = iTuple if keys: return pipe((k, iTuple(g),) for k, g in res) else: return pipe(iTuple(g) for k, g in res) def first(self): """ >>> iTuple.range(3).first() 0 """ return self[0] def last(self): """ >>> iTuple.range(3).last() 2 """ return self[-1] def pop_first(self): return self[1:] def pop_last(self): return self[:-1] def first_where(self, f, default = None): """ >>> iTuple.range(3).first_where(lambda v: v > 0) 1 """ for v in self: if f(v): return v return default def last_where(self, f, default = None): """ >>> iTuple.range(3).last_where(lambda v: v < 2) 1 """ for v in reversed(self): if f(v): return v return default def take(self, n): """ >>> iTuple.range(3).take(2) iTuple(0, 1) """ return self[:n] def tail(self, n): """ >>> iTuple.range(3).tail(2) iTuple(1, 2) """ return self[-n:] def reverse(self, lazy = False): """ >>> iTuple.range(3).reverse() iTuple(2, 1, 0) """ if lazy: return reversed(self) return type(self)(reversed(self)) def take_while(self, f, n = None, lazy = False): """ >>> iTuple.range(3).take_while(lambda v: v < 1) iTuple(0) """ def iter(): i = 0 for v in self: if f(v) and (n is None or i < n): yield v i += 1 else: return res = iter() return res if lazy else type(self)(res) def tail_while(self, f, n = None): """ >>> iTuple.range(3).tail_while(lambda v: v > 1) iTuple(2) """ i = 0 for v in reversed(self): if f(v) and (n is None or i < n): i += 1 else: break return self.tail(i) # NOTE: from as in, starting from first true # versus above, which is until first false def take_after(self, f, n = None, lazy = False): """ >>> iTuple.range(3).take_after(lambda v: v < 1) iTuple(1, 2) >>> iTuple.range(3).take_after(lambda v: v < 1, n = 1) iTuple(1) """ def iter(): i = 0 for v in self: if f(v): pass elif n is None or i < n: yield v i += 1 else: return res = iter() return res if lazy else type(self)(res) def tail_after(self, f, n = None): """ >>> iTuple.range(3).tail_after(lambda v: v < 2) iTuple(0, 1) >>> iTuple.range(3).tail_after(lambda v: v < 2, 1) iTuple(1) """ l = 0 r = 0 for v in reversed(self): if not f(v): l += 1 elif n is None or r < n: r += 1 else: break return self.tail(l + r).take(r) def islice(self, left = None, right = None): """ >>> iTuple.range(5).islice(1, 3) iTuple(1, 2) """ return self[left:right] def unique(self): """ >>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique() iTuple(1, 3, 2, 4) """ def iter(): seen: typing.Set = set() seen_add = seen.add seen_contains = seen.__contains__ for v in itertools.filterfalse(seen_contains, self): seen_add(v) yield v return type(self)(iter()) def sort(self, f = lambda v: v): """ >>> iTuple.range(3).reverse().sort() iTuple(0, 1, 2) >>> iTuple.range(3).sort() iTuple(0, 1, 2) """ return type(self)(sorted(self, key = f)) def accumulate(self, f, initial = None, lazy = False): """ >>> iTuple.range(3).accumulate(lambda acc, v: v) iTuple(0, 1, 2) >>> iTuple.range(3).accumulate(lambda acc, v: v, initial=0) iTuple(0, 0, 1, 2) >>> iTuple.range(3).accumulate(operator.add) iTuple(0, 1, 3) """ if lazy: return itertools.accumulate(self, func=f, initial=initial) return iTuple(itertools.accumulate( self, func=f, initial=initial )) def foldcum(self, *args, initial=None, **kwargs): """ >>> iTuple.range(3).foldcum(lambda acc, v: v) iTuple(0, 1, 2) >>> iTuple.range(3).foldcum(operator.add) iTuple(0, 1, 3) """ # res = [] # acc = initial # for x in self.iter(): # acc = f(acc, x) # res.append(acc) # return iTuple(tuple(res)) return self.accumulate(*args, **kwargs) def fold(self, f, initial=None): """ >>> iTuple.range(3).fold(lambda acc, v: v) 2 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 2 >>> iTuple.range(3).fold(operator.add) 3 """ # acc = initial # for v in self.iter(): # acc = f(acc, v) # return iTuple(tuple(acc)) if initial is not None: return functools.reduce(f, self, initial) else: return functools.reduce(f, self) def foldstar(self, f, initial=None): """ >>> iTuple.range(3).fold(lambda acc, v: v) 2 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 2 >>> iTuple.range(3).fold(operator.add) 3 """ # acc = initial # for v in self.iter(): # acc = f(acc, v) # return iTuple(tuple(acc)) fstar = lambda acc, v: f(acc, *v) if initial is not None: return functools.reduce(fstar, self, initial) else: return functools.reduce(fstar, self)
Ancestors
- builtins.tuple
Static methods
def empty()
-
Expand source code
@classmethod def empty(cls): return cls(tuple())
def from_columns(s)
-
Expand source code
@classmethod def from_columns(cls, s): return cls(s.columns)
def from_index(s)
-
Expand source code
@classmethod def from_index(cls, s): return cls(s.index)
def from_index_values(s)
-
Expand source code
@classmethod def from_index_values(cls, s): return cls(s.index.values)
def from_items(d)
-
>>> iTuple.from_items({i: i + 1 for i in range(2)}) iTuple((0, 1), (1, 2))
Expand source code
@classmethod def from_items(cls, d): """ >>> iTuple.from_items({i: i + 1 for i in range(2)}) iTuple((0, 1), (1, 2)) """ return cls(d.items())
def from_keys(d)
-
>>> iTuple.from_keys({i: i + 1 for i in range(2)}) iTuple(0, 1)
Expand source code
@classmethod def from_keys(cls, d): """ >>> iTuple.from_keys({i: i + 1 for i in range(2)}) iTuple(0, 1) """ return cls(d.keys())
def from_values(d)
-
>>> iTuple.from_values({i: i + 1 for i in range(2)}) iTuple(1, 2)
Expand source code
@classmethod def from_values(cls, d): """ >>> iTuple.from_values({i: i + 1 for i in range(2)}) iTuple(1, 2) """ return cls(d.values())
def none(n)
-
>>> iTuple.none(3) iTuple(None, None, None)
Expand source code
@classmethod def none(cls, n): """ >>> iTuple.none(3) iTuple(None, None, None) """ return cls((None for _ in range(n)))
def one(v)
-
Expand source code
@classmethod def one(cls, v): return cls((v,))
def range(*args, **kwargs)
-
>>> iTuple.range(3) iTuple(0, 1, 2)
Expand source code
@classmethod def range(cls, *args, **kwargs): """ >>> iTuple.range(3) iTuple(0, 1, 2) """ return cls(range(*args, **kwargs))
Methods
def accumulate(self, f, initial=None, lazy=False)
-
>>> iTuple.range(3).accumulate(lambda acc, v: v) iTuple(0, 1, 2) >>> iTuple.range(3).accumulate(lambda acc, v: v, initial=0) iTuple(0, 0, 1, 2) >>> iTuple.range(3).accumulate(operator.add) iTuple(0, 1, 3)
Expand source code
def accumulate(self, f, initial = None, lazy = False): """ >>> iTuple.range(3).accumulate(lambda acc, v: v) iTuple(0, 1, 2) >>> iTuple.range(3).accumulate(lambda acc, v: v, initial=0) iTuple(0, 0, 1, 2) >>> iTuple.range(3).accumulate(operator.add) iTuple(0, 1, 3) """ if lazy: return itertools.accumulate(self, func=f, initial=initial) return iTuple(itertools.accumulate( self, func=f, initial=initial ))
def all(self, f)
-
Expand source code
def all(self, f): return all(self.map(f, lazy=True))
def any(self, f)
-
Expand source code
def any(self, f): return any(self.map(f, lazy=True))
def append(self, value, *values)
-
>>> iTuple().append(1) iTuple(1) >>> iTuple.range(1).append(1) iTuple(0, 1) >>> iTuple.range(1).append(1, 2) iTuple(0, 1, 2) >>> iTuple.range(1).append(1, 2, 3) iTuple(0, 1, 2, 3) >>> iTuple.range(1).append(1, (2,)) iTuple(0, 1, (2,))
Expand source code
def append(self, value, *values): """ >>> iTuple().append(1) iTuple(1) >>> iTuple.range(1).append(1) iTuple(0, 1) >>> iTuple.range(1).append(1, 2) iTuple(0, 1, 2) >>> iTuple.range(1).append(1, 2, 3) iTuple(0, 1, 2, 3) >>> iTuple.range(1).append(1, (2,)) iTuple(0, 1, (2,)) """ return iTuple((*self, value, *values,))
def chunkby(self, f, lazy=False, keys=False, pipe=None)
-
>>> iTuple.range(3).chunkby(lambda x: x < 2) iTuple(iTuple(0, 1), iTuple(2)) >>> iTuple.range(3).chunkby( ... lambda x: x < 2, keys=True, pipe=fDict ... ) {True: iTuple(0, 1), False: iTuple(2)}
Expand source code
def chunkby( self, f, lazy = False, keys = False, pipe= None, ): """ >>> iTuple.range(3).chunkby(lambda x: x < 2) iTuple(iTuple(0, 1), iTuple(2)) >>> iTuple.range(3).chunkby( ... lambda x: x < 2, keys=True, pipe=fDict ... ) {True: iTuple(0, 1), False: iTuple(2)} """ # TODO: lazy no keys res = itertools.groupby(self, key=f) if lazy and keys and pipe is None: return res if pipe is None: pipe = iTuple if keys: return pipe((k, iTuple(g),) for k, g in res) else: return pipe(iTuple(g) for k, g in res)
def enumerate(self)
-
>>> iTuple.range(3).enumerate() iTuple((0, 0), (1, 1), (2, 2))
Expand source code
def enumerate(self): """ >>> iTuple.range(3).enumerate() iTuple((0, 0), (1, 1), (2, 2)) """ # TODO: allow lazy return iTuple(enumerate(self))
def extend(self, value, *values)
-
>>> iTuple.range(1).extend((1,)) iTuple(0, 1) >>> iTuple.range(1).extend([1]) iTuple(0, 1) >>> iTuple.range(1).extend([1], [2]) iTuple(0, 1, 2) >>> iTuple.range(1).extend([1], [[2]]) iTuple(0, 1, [2]) >>> iTuple.range(1).extend([1], [[2]], [2]) iTuple(0, 1, [2], 2)
Expand source code
def extend(self, value, *values): """ >>> iTuple.range(1).extend((1,)) iTuple(0, 1) >>> iTuple.range(1).extend([1]) iTuple(0, 1) >>> iTuple.range(1).extend([1], [2]) iTuple(0, 1, 2) >>> iTuple.range(1).extend([1], [[2]]) iTuple(0, 1, [2]) >>> iTuple.range(1).extend([1], [[2]], [2]) iTuple(0, 1, [2], 2) """ return iTuple(itertools.chain.from_iterable( (self, value, *values) ))
def filter(self, f, eq=None, lazy=False, **kws)
-
>>> iTuple.range(3).filter(lambda x: x > 1) iTuple(2)
Expand source code
def filter(self, f, eq = None, lazy = False, **kws): """ >>> iTuple.range(3).filter(lambda x: x > 1) iTuple(2) """ # res = [] # for v in self.iter(): # if f(v): # res.append(v) return type(self)(( v for v in self.iter() if f(v, **kws) ))
def filter_eq(self, v, f=None, eq=None, lazy=False)
-
>>> iTuple.range(3).filter_eq(1) iTuple(1)
Expand source code
def filter_eq(self, v, f = None, eq = None, lazy = False): """ >>> iTuple.range(3).filter_eq(1) iTuple(1) """ if f is None and eq is None: res = filter(lambda x: x == v, self) elif f is not None: res = filter(lambda x: f(x) == v, self) elif eq is not None: res = filter(lambda x: eq(x, v), self) elif f is not None and eq is not None: res = filter(lambda x: eq(f(x), v), self) else: assert False return res if lazy else type(self)(res)
def filterstar(self, f, eq=None, lazy=False, **kws)
-
>>> iTuple.range(3).filter(lambda x: x > 1) iTuple(2)
Expand source code
def filterstar(self, f, eq = None, lazy = False, **kws): """ >>> iTuple.range(3).filter(lambda x: x > 1) iTuple(2) """ # res = [] # for v in self.iter(): # if f(v): # res.append(v) return type(self)(( v for v in self.iter() if f(*v, **kws) ))
def first(self)
-
>>> iTuple.range(3).first() 0
Expand source code
def first(self): """ >>> iTuple.range(3).first() 0 """ return self[0]
def first_where(self, f, default=None)
-
>>> iTuple.range(3).first_where(lambda v: v > 0) 1
Expand source code
def first_where(self, f, default = None): """ >>> iTuple.range(3).first_where(lambda v: v > 0) 1 """ for v in self: if f(v): return v return default
def flatten(self)
-
>>> iTuple.range(3).map(lambda x: [x]).flatten() iTuple(0, 1, 2)
Expand source code
def flatten(self): """ >>> iTuple.range(3).map(lambda x: [x]).flatten() iTuple(0, 1, 2) """ return iTuple(itertools.chain(*self))
def fold(self, f, initial=None)
-
>>> iTuple.range(3).fold(lambda acc, v: v) 2 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 2 >>> iTuple.range(3).fold(operator.add) 3
Expand source code
def fold(self, f, initial=None): """ >>> iTuple.range(3).fold(lambda acc, v: v) 2 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 2 >>> iTuple.range(3).fold(operator.add) 3 """ # acc = initial # for v in self.iter(): # acc = f(acc, v) # return iTuple(tuple(acc)) if initial is not None: return functools.reduce(f, self, initial) else: return functools.reduce(f, self)
def foldcum(self, *args, initial=None, **kwargs)
-
>>> iTuple.range(3).foldcum(lambda acc, v: v) iTuple(0, 1, 2) >>> iTuple.range(3).foldcum(operator.add) iTuple(0, 1, 3)
Expand source code
def foldcum(self, *args, initial=None, **kwargs): """ >>> iTuple.range(3).foldcum(lambda acc, v: v) iTuple(0, 1, 2) >>> iTuple.range(3).foldcum(operator.add) iTuple(0, 1, 3) """ # res = [] # acc = initial # for x in self.iter(): # acc = f(acc, x) # res.append(acc) # return iTuple(tuple(res)) return self.accumulate(*args, **kwargs)
def foldstar(self, f, initial=None)
-
>>> iTuple.range(3).fold(lambda acc, v: v) 2 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 2 >>> iTuple.range(3).fold(operator.add) 3
Expand source code
def foldstar(self, f, initial=None): """ >>> iTuple.range(3).fold(lambda acc, v: v) 2 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 2 >>> iTuple.range(3).fold(operator.add) 3 """ # acc = initial # for v in self.iter(): # acc = f(acc, v) # return iTuple(tuple(acc)) fstar = lambda acc, v: f(acc, *v) if initial is not None: return functools.reduce(fstar, self, initial) else: return functools.reduce(fstar, self)
def get(self, i)
-
Expand source code
def get(self, i): if isinstance(i, slice): return type(self)(self[i]) return self[i]
def groupby(self, f, lazy=False, keys=False, pipe=None)
-
>>> iTuple.range(3).groupby(lambda x: x < 2) iTuple(iTuple(2), iTuple(0, 1)) >>> iTuple.range(3).groupby( ... lambda x: x < 2, keys=True, pipe=fDict ... ) {False: iTuple(2), True: iTuple(0, 1)}
Expand source code
def groupby( self, f, lazy = False, keys = False, pipe = None ): """ >>> iTuple.range(3).groupby(lambda x: x < 2) iTuple(iTuple(2), iTuple(0, 1)) >>> iTuple.range(3).groupby( ... lambda x: x < 2, keys=True, pipe=fDict ... ) {False: iTuple(2), True: iTuple(0, 1)} """ res = ( self.chunkby(f, keys=True) .sort(f = lambda kg: kg[0]) .chunkby(lambda kg: kg[0], keys = True) .map(lambda k_kgs: ( k_kgs[0], k_kgs[1].mapstar(lambda k, g: g).flatten(), )) ) if pipe is None: pipe = iTuple if keys: return pipe((k, iTuple(g),) for k, g in res) else: return pipe(iTuple(g) for k, g in res)
def is_none(self)
-
Expand source code
def is_none(self): return self.filter(lambda v: v is None)
def islice(self, left=None, right=None)
-
>>> iTuple.range(5).islice(1, 3) iTuple(1, 2)
Expand source code
def islice(self, left = None, right = None): """ >>> iTuple.range(5).islice(1, 3) iTuple(1, 2) """ return self[left:right]
def iter(self)
-
>>> for x in iTuple.range(3).iter(): print(x) 0 1 2
Expand source code
def iter(self): """ >>> for x in iTuple.range(3).iter(): print(x) 0 1 2 """ return iter(self)
def last(self)
-
>>> iTuple.range(3).last() 2
Expand source code
def last(self): """ >>> iTuple.range(3).last() 2 """ return self[-1]
def last_where(self, f, default=None)
-
>>> iTuple.range(3).last_where(lambda v: v < 2) 1
Expand source code
def last_where(self, f, default = None): """ >>> iTuple.range(3).last_where(lambda v: v < 2) 1 """ for v in reversed(self): if f(v): return v return default
def len(self)
-
>>> iTuple.range(3).len() 3
Expand source code
def len(self): """ >>> iTuple.range(3).len() 3 """ return len(self)
def len_range(self)
-
>>> iTuple.range(3).len() 3
Expand source code
def len_range(self): """ >>> iTuple.range(3).len() 3 """ return type(self).range(self.len())
def map(self, f, *iterables, at=None, lazy=False) ‑> iTuple
-
>>> iTuple.range(3).map(lambda x: x * 2) iTuple(0, 2, 4)
Expand source code
def map( self, f, *iterables, at = None, lazy = False ) -> iTuple: """ >>> iTuple.range(3).map(lambda x: x * 2) iTuple(0, 2, 4) """ # if lazy and at is None: # return map(f, self, *iterables) if at is None: return iTuple(map(f, self, *iterables)) elif isinstance(at, int): return iTuple(map( f, *iterables[:at], self, *iterables[at:] )) elif isinstance(at, str): return iTuple(map( f, *iterables, **{at: self} )) else: assert False, at
def mapstar(self, f)
-
Expand source code
def mapstar(self, f): return iTuple(itertools.starmap(f, self))
def not_none(self)
-
Expand source code
def not_none(self): return self.filter(lambda v: v is not None)
def partial(self, f, *args, **kwargs)
-
>>> f = iTuple.range(2).partial( ... lambda it, v: it.map(lambda x: x * v) ... ) >>> f(2) iTuple(0, 2) >>> f(3) iTuple(0, 3)
Expand source code
def partial(self, f, *args, **kwargs): """ >>> f = iTuple.range(2).partial( ... lambda it, v: it.map(lambda x: x * v) ... ) >>> f(2) iTuple(0, 2) >>> f(3) iTuple(0, 3) """ return functools.partial(f, self, *args, **kwargs)
def pipe(self, f, *args, at=None, **kwargs)
-
>>> iTuple.range(2).pipe(lambda it: it) iTuple(0, 1) >>> iTuple.range(2).pipe( ... lambda it, v: it.map(lambda x: x * v), 2 ... ) iTuple(0, 2)
Expand source code
def pipe(self, f, *args, at = None, **kwargs): """ >>> iTuple.range(2).pipe(lambda it: it) iTuple(0, 1) >>> iTuple.range(2).pipe( ... lambda it, v: it.map(lambda x: x * v), 2 ... ) iTuple(0, 2) """ return pipe(f, self, *args, at = at, **kwargs)
def pop_first(self)
-
Expand source code
def pop_first(self): return self[1:]
def pop_last(self)
-
Expand source code
def pop_last(self): return self[:-1]
def prepend(self, value, *values)
-
>>> iTuple().prepend(1) iTuple(1) >>> iTuple.range(1).prepend(1) iTuple(1, 0) >>> iTuple.range(1).prepend(1, 2) iTuple(1, 2, 0) >>> iTuple.range(1).prepend(1, 2, 3) iTuple(1, 2, 3, 0) >>> iTuple.range(1).prepend(1, (2,)) iTuple(1, (2,), 0)
Expand source code
def prepend(self, value, *values): """ >>> iTuple().prepend(1) iTuple(1) >>> iTuple.range(1).prepend(1) iTuple(1, 0) >>> iTuple.range(1).prepend(1, 2) iTuple(1, 2, 0) >>> iTuple.range(1).prepend(1, 2, 3) iTuple(1, 2, 3, 0) >>> iTuple.range(1).prepend(1, (2,)) iTuple(1, (2,), 0) """ return iTuple((value, *values, *self,))
def pretend(self, value, *values)
-
>>> iTuple.range(1).pretend((1,)) iTuple(1, 0) >>> iTuple.range(1).pretend([1]) iTuple(1, 0) >>> iTuple.range(1).pretend([1], [2]) iTuple(1, 2, 0) >>> iTuple.range(1).pretend([1], [[2]]) iTuple(1, [2], 0) >>> iTuple.range(1).pretend([1], [[2]], [2]) iTuple(1, [2], 2, 0)
Expand source code
def pretend(self, value, *values): """ >>> iTuple.range(1).pretend((1,)) iTuple(1, 0) >>> iTuple.range(1).pretend([1]) iTuple(1, 0) >>> iTuple.range(1).pretend([1], [2]) iTuple(1, 2, 0) >>> iTuple.range(1).pretend([1], [[2]]) iTuple(1, [2], 0) >>> iTuple.range(1).pretend([1], [[2]], [2]) iTuple(1, [2], 2, 0) """ return iTuple(itertools.chain.from_iterable( (value, *values, self) ))
def reverse(self, lazy=False)
-
>>> iTuple.range(3).reverse() iTuple(2, 1, 0)
Expand source code
def reverse(self, lazy = False): """ >>> iTuple.range(3).reverse() iTuple(2, 1, 0) """ if lazy: return reversed(self) return type(self)(reversed(self))
def sort(self, f=<function iTuple.<lambda>>)
-
>>> iTuple.range(3).reverse().sort() iTuple(0, 1, 2) >>> iTuple.range(3).sort() iTuple(0, 1, 2)
Expand source code
def sort(self, f = lambda v: v): """ >>> iTuple.range(3).reverse().sort() iTuple(0, 1, 2) >>> iTuple.range(3).sort() iTuple(0, 1, 2) """ return type(self)(sorted(self, key = f))
def tail(self, n)
-
>>> iTuple.range(3).tail(2) iTuple(1, 2)
Expand source code
def tail(self, n): """ >>> iTuple.range(3).tail(2) iTuple(1, 2) """ return self[-n:]
def tail_after(self, f, n=None)
-
>>> iTuple.range(3).tail_after(lambda v: v < 2) iTuple(0, 1) >>> iTuple.range(3).tail_after(lambda v: v < 2, 1) iTuple(1)
Expand source code
def tail_after(self, f, n = None): """ >>> iTuple.range(3).tail_after(lambda v: v < 2) iTuple(0, 1) >>> iTuple.range(3).tail_after(lambda v: v < 2, 1) iTuple(1) """ l = 0 r = 0 for v in reversed(self): if not f(v): l += 1 elif n is None or r < n: r += 1 else: break return self.tail(l + r).take(r)
def tail_while(self, f, n=None)
-
>>> iTuple.range(3).tail_while(lambda v: v > 1) iTuple(2)
Expand source code
def tail_while(self, f, n = None): """ >>> iTuple.range(3).tail_while(lambda v: v > 1) iTuple(2) """ i = 0 for v in reversed(self): if f(v) and (n is None or i < n): i += 1 else: break return self.tail(i)
def take(self, n)
-
>>> iTuple.range(3).take(2) iTuple(0, 1)
Expand source code
def take(self, n): """ >>> iTuple.range(3).take(2) iTuple(0, 1) """ return self[:n]
def take_after(self, f, n=None, lazy=False)
-
>>> iTuple.range(3).take_after(lambda v: v < 1) iTuple(1, 2) >>> iTuple.range(3).take_after(lambda v: v < 1, n = 1) iTuple(1)
Expand source code
def take_after(self, f, n = None, lazy = False): """ >>> iTuple.range(3).take_after(lambda v: v < 1) iTuple(1, 2) >>> iTuple.range(3).take_after(lambda v: v < 1, n = 1) iTuple(1) """ def iter(): i = 0 for v in self: if f(v): pass elif n is None or i < n: yield v i += 1 else: return res = iter() return res if lazy else type(self)(res)
def take_while(self, f, n=None, lazy=False)
-
>>> iTuple.range(3).take_while(lambda v: v < 1) iTuple(0)
Expand source code
def take_while(self, f, n = None, lazy = False): """ >>> iTuple.range(3).take_while(lambda v: v < 1) iTuple(0) """ def iter(): i = 0 for v in self: if f(v) and (n is None or i < n): yield v i += 1 else: return res = iter() return res if lazy else type(self)(res)
def unique(self)
-
>>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique() iTuple(1, 3, 2, 4)
Expand source code
def unique(self): """ >>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique() iTuple(1, 3, 2, 4) """ def iter(): seen: typing.Set = set() seen_add = seen.add seen_contains = seen.__contains__ for v in itertools.filterfalse(seen_contains, self): seen_add(v) yield v return type(self)(iter())
def zip(self, *itrs, lazy=False, at=None)
-
>>> iTuple([[1, 1], [2, 2], [3, 3]]).zip() iTuple((1, 2, 3), (1, 2, 3)) >>> iTuple([iTuple.range(3), iTuple.range(1, 4)]).zip() iTuple((0, 1), (1, 2), (2, 3)) >>> iTuple.range(3).zip(iTuple.range(1, 4)) iTuple((0, 1), (1, 2), (2, 3))
Expand source code
def zip(self, *itrs, lazy = False, at = None): """ >>> iTuple([[1, 1], [2, 2], [3, 3]]).zip() iTuple((1, 2, 3), (1, 2, 3)) >>> iTuple([iTuple.range(3), iTuple.range(1, 4)]).zip() iTuple((0, 1), (1, 2), (2, 3)) >>> iTuple.range(3).zip(iTuple.range(1, 4)) iTuple((0, 1), (1, 2), (2, 3)) """ if len(itrs) == 0: res = zip(*self) elif at is None: res = zip(self, *itrs) elif isinstance(at, int): res = zip(*itrs[:at], self, *itrs[at:]) else: assert False, at return res if lazy else iTuple(res)
class nTuple
-
Helper class that provides a standard way to create an ABC using inheritance.
Expand source code
class nTuple(abc.ABC): @abc.abstractmethod def __abstract__(self): # NOTE: here to prevent initialise instances of this # but rather use the decorator and typing.NamedTuple return @staticmethod def pipe(obj, f, *args, at = None, **kwargs): """ >>> _Example = _Example(1, "a") >>> _Example.pipe(lambda a, b: a, None) _Example(x=1, s='a', it=iTuple()) >>> _Example.pipe(lambda a, b: a, None, at = 1) >>> _Example.pipe(lambda a, b: a, None, at = 'b') >>> _Example.pipe(lambda a, b: a, a=None, at = 'b') >>> _Example.pipe(lambda a, b: a, b=None, at = 'a') _Example(x=1, s='a', it=iTuple()) >>> _Example.pipe(lambda a, b: a, None, at = 0) _Example(x=1, s='a', it=iTuple()) """ return pipe(f, obj, *args, at = at, **kwargs) @staticmethod def partial(obj, f, *args, **kwargs): return functools.partial(f, obj, *args, **kwargs) @classmethod def is_subclass(cls, t): """ >>> nTuple.is_subclass(tuple) False >>> nTuple.is_subclass(_Example(1, "a")) False >>> nTuple.is_subclass(_Example) True """ try: is_sub = issubclass(t, tuple) except: is_sub = False return ( is_sub and hasattr(t, "cls") and hasattr(t, "pipe") and hasattr(t, "partial") ) @classmethod def is_instance(cls, obj): """ >>> nTuple.is_instance(tuple) False >>> nTuple.is_instance(_Example) False >>> nTuple.is_instance(_Example(1, "a")) True """ return ( cls.is_subclass(type(obj)) and hasattr(obj, '_asdict') and hasattr(obj, '_fields') ) @staticmethod def annotations(obj): """ >>> ex = _Example(1, "a") >>> ex.pipe(ex.cls.annotations) {'x': ForwardRef('int'), 's': ForwardRef('str'), 'it': ForwardRef('iTuple')} """ return fDict(obj.__annotations__) @classmethod def as_dict(cls, obj): """ >>> ex = _Example(1, "a") >>> ex.pipe(ex.cls.as_dict) {'x': 1, 's': 'a', 'it': iTuple()} """ return fDict(obj._asdict()) @classmethod def decorate(meta, **methods): def decorator(cls): cls.pipe = meta.pipe cls.partial = meta.partial cls.cls = meta for k, f in methods.items(): setattr(cls, k, f) return cls return decorator @classmethod def enum(meta, cls): cls = meta.decorate()(cls) return functools.lru_cache(maxsize=1)(cls)
Ancestors
- abc.ABC
Static methods
def annotations(obj)
-
>>> ex = _Example(1, "a") >>> ex.pipe(ex.cls.annotations) {'x': ForwardRef('int'), 's': ForwardRef('str'), 'it': ForwardRef('iTuple')}
Expand source code
@staticmethod def annotations(obj): """ >>> ex = _Example(1, "a") >>> ex.pipe(ex.cls.annotations) {'x': ForwardRef('int'), 's': ForwardRef('str'), 'it': ForwardRef('iTuple')} """ return fDict(obj.__annotations__)
def as_dict(obj)
-
>>> ex = _Example(1, "a") >>> ex.pipe(ex.cls.as_dict) {'x': 1, 's': 'a', 'it': iTuple()}
Expand source code
@classmethod def as_dict(cls, obj): """ >>> ex = _Example(1, "a") >>> ex.pipe(ex.cls.as_dict) {'x': 1, 's': 'a', 'it': iTuple()} """ return fDict(obj._asdict())
def decorate(**methods)
-
Expand source code
@classmethod def decorate(meta, **methods): def decorator(cls): cls.pipe = meta.pipe cls.partial = meta.partial cls.cls = meta for k, f in methods.items(): setattr(cls, k, f) return cls return decorator
def enum(cls)
-
Expand source code
@classmethod def enum(meta, cls): cls = meta.decorate()(cls) return functools.lru_cache(maxsize=1)(cls)
def is_instance(obj)
-
>>> nTuple.is_instance(tuple) False >>> nTuple.is_instance(_Example) False >>> nTuple.is_instance(_Example(1, "a")) True
Expand source code
@classmethod def is_instance(cls, obj): """ >>> nTuple.is_instance(tuple) False >>> nTuple.is_instance(_Example) False >>> nTuple.is_instance(_Example(1, "a")) True """ return ( cls.is_subclass(type(obj)) and hasattr(obj, '_asdict') and hasattr(obj, '_fields') )
def is_subclass(t)
-
>>> nTuple.is_subclass(tuple) False >>> nTuple.is_subclass(_Example(1, "a")) False >>> nTuple.is_subclass(_Example) True
Expand source code
@classmethod def is_subclass(cls, t): """ >>> nTuple.is_subclass(tuple) False >>> nTuple.is_subclass(_Example(1, "a")) False >>> nTuple.is_subclass(_Example) True """ try: is_sub = issubclass(t, tuple) except: is_sub = False return ( is_sub and hasattr(t, "cls") and hasattr(t, "pipe") and hasattr(t, "partial") )
def partial(obj, f, *args, **kwargs)
-
Expand source code
@staticmethod def partial(obj, f, *args, **kwargs): return functools.partial(f, obj, *args, **kwargs)
def pipe(obj, f, *args, at=None, **kwargs)
-
>>> _Example = _Example(1, "a") >>> _Example.pipe(lambda a, b: a, None) _Example(x=1, s='a', it=iTuple()) >>> _Example.pipe(lambda a, b: a, None, at = 1) >>> _Example.pipe(lambda a, b: a, None, at = 'b') >>> _Example.pipe(lambda a, b: a, a=None, at = 'b') >>> _Example.pipe(lambda a, b: a, b=None, at = 'a') _Example(x=1, s='a', it=iTuple()) >>> _Example.pipe(lambda a, b: a, None, at = 0) _Example(x=1, s='a', it=iTuple())
Expand source code
@staticmethod def pipe(obj, f, *args, at = None, **kwargs): """ >>> _Example = _Example(1, "a") >>> _Example.pipe(lambda a, b: a, None) _Example(x=1, s='a', it=iTuple()) >>> _Example.pipe(lambda a, b: a, None, at = 1) >>> _Example.pipe(lambda a, b: a, None, at = 'b') >>> _Example.pipe(lambda a, b: a, a=None, at = 'b') >>> _Example.pipe(lambda a, b: a, b=None, at = 'a') _Example(x=1, s='a', it=iTuple()) >>> _Example.pipe(lambda a, b: a, None, at = 0) _Example(x=1, s='a', it=iTuple()) """ return pipe(f, obj, *args, at = at, **kwargs)