xtuples.xtuples
1# --------------------------------------------------------------- 2 3import abc 4import typing 5import dataclasses 6import collections 7 8import operator 9import itertools 10import functools 11 12# --------------------------------------------------------------- 13 14# NOTE: at worst, not worse, than an un-optimised canonical solution 15 16# where I cribbed from the itertools recipes (and other python docs), all credit to the original authors. 17 18# where i didn't, i probably should have. 19 20# --------------------------------------------------------------- 21 22REGISTRY = {} 23 24# --------------------------------------------------------------- 25 26def pipe(f, obj, *args, at = None, **kwargs): 27 if at is None: 28 return f(obj, *args, **kwargs) 29 elif isinstance(at, int): 30 return f(*args[:at], obj, *args[at:], **kwargs) 31 elif isinstance(at, str): 32 return f(*args, **{at: obj}, **kwargs) 33 else: 34 assert False, at 35 36# --------------------------------------------------------------- 37 38 39# TODO: some kind of validation placeholder? 40# called in init, eg. quarter in [1 .. 4] 41 42class nTuple(abc.ABC): 43 44 @abc.abstractmethod 45 def __abstract__(self): 46 # NOTE: here to prevent initialise instances of this 47 # but rather use the decorator and typing.NamedTuple 48 return 49 50 @staticmethod 51 def pipe(obj, f, *args, at = None, **kwargs): 52 """ 53 >>> example = Example(1, "a") 54 >>> example.pipe(lambda a, b: a, None) 55 Example(x=1, s='a', it=iTuple()) 56 >>> example.pipe(lambda a, b: a, None, at = 1) 57 >>> example.pipe(lambda a, b: a, None, at = 'b') 58 >>> example.pipe(lambda a, b: a, a=None, at = 'b') 59 >>> example.pipe(lambda a, b: a, b=None, at = 'a') 60 Example(x=1, s='a', it=iTuple()) 61 >>> example.pipe(lambda a, b: a, None, at = 0) 62 Example(x=1, s='a', it=iTuple()) 63 """ 64 return pipe(f, obj, *args, at = at, **kwargs) 65 66 @staticmethod 67 def partial(obj, f, *args, **kwargs): 68 return functools.partial(f, obj, *args, **kwargs) 69 70 @classmethod 71 def is_subclass(cls, t): 72 """ 73 >>> nTuple.is_subclass(tuple) 74 False 75 >>> nTuple.is_subclass(Example(1, "a")) 76 False 77 >>> nTuple.is_subclass(Example) 78 True 79 """ 80 try: 81 is_sub = issubclass(t, tuple) 82 except: 83 is_sub = False 84 return ( 85 is_sub and 86 hasattr(t, "cls") and 87 hasattr(t, "pipe") and 88 hasattr(t, "partial") 89 ) 90 91 @classmethod 92 def is_instance(cls, obj): 93 """ 94 >>> nTuple.is_instance(tuple) 95 False 96 >>> nTuple.is_instance(Example) 97 False 98 >>> nTuple.is_instance(Example(1, "a")) 99 True 100 """ 101 return ( 102 cls.is_subclass(type(obj)) and 103 hasattr(obj, '_asdict') and 104 hasattr(obj, '_fields') 105 ) 106 107 108 @staticmethod 109 def annotations(obj): 110 """ 111 >>> ex = Example(1, "a") 112 >>> ex.pipe(ex.cls.annotations) 113 {'x': <class 'int'>, 's': <class 'str'>, 'it': <class 'xtuples.xtuples.iTuple'>} 114 """ 115 return fDict(obj.__annotations__) 116 117 @classmethod 118 def as_dict(cls, obj): 119 """ 120 >>> ex = Example(1, "a") 121 >>> ex.pipe(ex.cls.as_dict) 122 {'x': 1, 's': 'a', 'it': iTuple()} 123 """ 124 return fDict(obj._asdict()) 125 126 @classmethod 127 def decorate(meta, cls): 128 assert cls.__name__ not in REGISTRY 129 cls.pipe = meta.pipe 130 cls.partial = meta.partial 131 cls.cls = meta 132 REGISTRY[cls.__name__] = cls 133 return cls 134 135# --------------------------------------------------------------- 136 137class fDict(collections.UserDict): 138 __slots__ = () 139 140 data: dict 141 142 def pipe(self, f, *args, at=None, **kwargs): 143 """ 144 >>> fDict({0: 1}).pipe(lambda d: d.map_values( 145 ... lambda v: v + 1 146 ... )) 147 {0: 2} 148 """ 149 res = pipe(f, self, *args, at = at, **kwargs) 150 if isinstance(res, dict): 151 return fDict(res) 152 return res 153 154 def partial(self, f, *args, **kwargs): 155 """ 156 >>> f = fDict({0: 1}).partial( 157 ... lambda d, n: d.map_values(lambda v: v + n) 158 ... ) 159 >>> f(1) 160 {0: 2} 161 >>> f(2) 162 {0: 3} 163 """ 164 return functools.partial(f, self, *args, **kwargs) 165 166 def keys_tuple(self): 167 """ 168 >>> fDict({0: 1}).keys_tuple() 169 iTuple(0) 170 """ 171 return iTuple.from_keys(self) 172 173 def values_tuple(self): 174 """ 175 >>> fDict({0: 1}).values_tuple() 176 iTuple(1) 177 """ 178 return iTuple.from_values(self) 179 180 def items_tuple(self): 181 """ 182 >>> fDict({0: 1}).items_tuple() 183 iTuple((0, 1)) 184 """ 185 return iTuple.from_items(self) 186 187 # NOTE: we have separate map implementations 188 # as they are constant size, dict to dict 189 # other iterator functions should use iTuple (from the above) 190 191 def map_keys(self, f, *args, **kwargs): 192 """ 193 >>> fDict({0: 1}).map_keys(lambda v: v + 1) 194 {1: 1} 195 """ 196 return fDict( 197 (f(k, *args, **kwargs), v) for k, v in self.items() 198 ) 199 200 def map_values(self, f, *args, **kwargs): 201 """ 202 >>> fDict({0: 1}).map_values(lambda v: v + 1) 203 {0: 2} 204 """ 205 return fDict( 206 (k, f(v, *args, **kwargs)) for k, v in self.items() 207 ) 208 209 def map_items(self, f, *args, **kwargs): 210 """ 211 >>> fDict({0: 1}).map_items(lambda k, v: (v, k)) 212 {1: 0} 213 """ 214 return fDict( 215 f(k, v, *args, **kwargs) for k, v in self.items() 216 ) 217 218 def invert(self): 219 """ 220 >>> fDict({0: 1}).invert() 221 {1: 0} 222 """ 223 return fDict((v, k) for k, v in self.items()) 224 225# --------------------------------------------------------------- 226 227@dataclasses.dataclass(init = False, repr=True) 228class iTuple(collections.UserList, tuple): # type: ignore 229 __slots__ = () 230 231 data: tuple # type: ignore 232 233 # ----- 234 235 @staticmethod 236 def __new__(cls, data = None): 237 # NOTE: we use cls not array 238 # so sub-classing *does* change identity 239 if isinstance(data, cls): 240 return data 241 return super().__new__(cls, data=data) 242 243 @staticmethod 244 def wrap_tuple(data): 245 return data if isinstance(data, tuple) else tuple(data) 246 247 def __init__(self, data = None): 248 # TODO: option for lazy init? 249 self.data = ( 250 tuple() if data is None 251 else self.wrap_tuple(data) 252 ) 253 254 def __repr__(self): 255 s = super().__repr__() 256 return "{}({})".format( 257 type(self).__name__, 258 s[1:-2 if s[-2] == "," else -1], 259 ) 260 261 def __hash__(self): 262 return hash(self.data) 263 264 @classmethod 265 def decorate(meta, cls): 266 assert cls.__name__ not in REGISTRY 267 REGISTRY[cls.__name__] = cls 268 return cls 269 270 # ----- 271 272 @classmethod 273 def range(cls, *args, **kwargs): 274 """ 275 >>> iTuple.range(3) 276 iTuple(0, 1, 2) 277 """ 278 return cls(range(*args, **kwargs)) 279 280 @classmethod 281 def from_keys(cls, d): 282 """ 283 >>> iTuple.from_keys({i: i + 1 for i in range(2)}) 284 iTuple(0, 1) 285 """ 286 return cls(d.keys()) 287 288 @classmethod 289 def from_values(cls, d): 290 """ 291 >>> iTuple.from_values({i: i + 1 for i in range(2)}) 292 iTuple(1, 2) 293 """ 294 return cls(d.values()) 295 296 @classmethod 297 def from_items(cls, d): 298 """ 299 >>> iTuple.from_items({i: i + 1 for i in range(2)}) 300 iTuple((0, 1), (1, 2)) 301 """ 302 return cls(d.items()) 303 304 # ----- 305 306 def pipe(self, f, *args, at = None, **kwargs): 307 """ 308 >>> iTuple.range(2).pipe(lambda it: it) 309 iTuple(0, 1) 310 >>> iTuple.range(2).pipe( 311 ... lambda it, v: it.map(lambda x: x * v), 2 312 ... ) 313 iTuple(0, 2) 314 """ 315 return pipe(f, self, *args, at = at, **kwargs) 316 317 def partial(self, f, *args, **kwargs): 318 """ 319 >>> f = iTuple.range(2).partial( 320 ... lambda it, v: it.map(lambda x: x * v) 321 ... ) 322 >>> f(2) 323 iTuple(0, 2) 324 >>> f(3) 325 iTuple(0, 3) 326 """ 327 return functools.partial(f, self, *args, **kwargs) 328 329 # ----- 330 331 def len(self): 332 """ 333 >>> iTuple.range(3).len() 334 3 335 """ 336 return len(self) 337 338 def append(self, value, *values): 339 """ 340 >>> iTuple.range(1).append(1) 341 iTuple(0, 1) 342 >>> iTuple.range(1).append(1, 2) 343 iTuple(0, 1, 2) 344 >>> iTuple.range(1).append(1, 2, 3) 345 iTuple(0, 1, 2, 3) 346 >>> iTuple.range(1).append(1, (2,)) 347 iTuple(0, 1, (2,)) 348 """ 349 return self + (value, *values) 350 351 def prepend(self, value, *values): 352 """ 353 >>> iTuple.range(1).prepend(1) 354 iTuple(1, 0) 355 >>> iTuple.range(1).prepend(1, 2) 356 iTuple(1, 2, 0) 357 >>> iTuple.range(1).prepend(1, 2, 3) 358 iTuple(1, 2, 3, 0) 359 >>> iTuple.range(1).prepend(1, (2,)) 360 iTuple(1, (2,), 0) 361 """ 362 return (value, *values) + self 363 364 def zip(self, *itrs, lazy = False): 365 """ 366 >>> iTuple([[1, 1], [2, 2], [3, 3]]).zip() 367 iTuple((1, 2, 3), (1, 2, 3)) 368 >>> iTuple([iTuple.range(3), iTuple.range(1, 4)]).zip() 369 iTuple((0, 1), (1, 2), (2, 3)) 370 >>> iTuple.range(3).zip(iTuple.range(1, 4)) 371 iTuple((0, 1), (1, 2), (2, 3)) 372 """ 373 if len(itrs) == 0: 374 res = zip(*self) 375 else: 376 res = zip(self, *itrs) 377 return res if lazy else iTuple(data=res) 378 379 def flatten(self): 380 """ 381 >>> iTuple.range(3).map(lambda x: [x]).flatten() 382 iTuple(0, 1, 2) 383 """ 384 return iTuple(itertools.chain(*self)) 385 386 def extend(self, value, *values): 387 """ 388 >>> iTuple.range(1).extend((1,)) 389 iTuple(0, 1) 390 >>> iTuple.range(1).extend([1]) 391 iTuple(0, 1) 392 >>> iTuple.range(1).extend([1], [2]) 393 iTuple(0, 1, 2) 394 >>> iTuple.range(1).extend([1], [[2]]) 395 iTuple(0, 1, [2]) 396 >>> iTuple.range(1).extend([1], [[2]], [2]) 397 iTuple(0, 1, [2], 2) 398 """ 399 return iTuple(itertools.chain.from_iterable( 400 (self, value, *values) 401 )) 402 403 def pretend(self, value, *values): 404 """ 405 >>> iTuple.range(1).pretend((1,)) 406 iTuple(1, 0) 407 >>> iTuple.range(1).pretend([1]) 408 iTuple(1, 0) 409 >>> iTuple.range(1).pretend([1], [2]) 410 iTuple(1, 2, 0) 411 >>> iTuple.range(1).pretend([1], [[2]]) 412 iTuple(1, [2], 0) 413 >>> iTuple.range(1).pretend([1], [[2]], [2]) 414 iTuple(1, [2], 2, 0) 415 """ 416 return iTuple(itertools.chain.from_iterable( 417 (value, *values, self) 418 )) 419 420 def filter_eq(self, v, f = None, eq = None, lazy = False): 421 """ 422 >>> iTuple.range(3).filter_eq(1) 423 iTuple(1) 424 """ 425 if f is None and eq is None: 426 res = filter(lambda x: x == v, self) 427 elif f is not None: 428 res = filter(lambda x: f(x) == v, self) 429 elif eq is not None: 430 res = filter(lambda x: eq(x, v), self) 431 else: 432 res = filter(lambda x: eq(f(x), v), self) 433 return res if lazy else type(self)(data=res) 434 435 def filter(self, f, eq = None, lazy = False): 436 """ 437 >>> iTuple.range(3).filter(lambda x: x > 1) 438 iTuple(2) 439 """ 440 return self.filter_eq(True, f = f, eq = eq, lazy = lazy) 441 442 def map(self, f, *iterables, lazy = False): 443 """ 444 >>> iTuple.range(3).map(lambda x: x * 2) 445 iTuple(0, 2, 4) 446 """ 447 res = map(f, self, *iterables) 448 return res if lazy else iTuple(data=res) 449 450 def enumerate(self): 451 """ 452 >>> iTuple.range(3).enumerate() 453 iTuple((0, 0), (1, 1), (2, 2)) 454 """ 455 # TODO: allow lazy 456 return iTuple(enumerate(self)) 457 458 def groupby( 459 self, 460 f, 461 lazy = False, 462 keys = False, 463 pipe= None, 464 ): 465 """ 466 >>> iTuple.range(3).groupby(lambda x: x < 2) 467 iTuple((0, 1), (2,)) 468 >>> iTuple.range(3).groupby( 469 ... lambda x: x < 2, keys=True, pipe=fDict 470 ... ) 471 {True: (0, 1), False: (2,)} 472 """ 473 # TODO: lazy no keys 474 res = itertools.groupby(self, key=f) 475 if lazy and keys and pipe is None: 476 return res 477 if pipe is None: 478 pipe = iTuple 479 if keys: 480 return pipe((k, tuple(g),) for k, g in res) 481 else: 482 return pipe(tuple(g) for k, g in res) 483 484 def first(self): 485 """ 486 >>> iTuple.range(3).first() 487 0 488 """ 489 return self[0] 490 491 def last(self): 492 """ 493 >>> iTuple.range(3).last() 494 2 495 """ 496 return self[-1] 497 498 def first_where(self, f): 499 """ 500 >>> iTuple.range(3).first_where(lambda v: v > 0) 501 1 502 """ 503 for v in self: 504 if f(v): 505 return v 506 return None 507 508 def last_where(self, f): 509 """ 510 >>> iTuple.range(3).last_where(lambda v: v < 2) 511 1 512 """ 513 for v in reversed(self): 514 if f(v): 515 return v 516 return None 517 518 def take(self, n): 519 """ 520 >>> iTuple.range(3).take(2) 521 iTuple(0, 1) 522 """ 523 return self[:n] 524 525 def tail(self, n): 526 """ 527 >>> iTuple.range(3).tail(2) 528 iTuple(1, 2) 529 """ 530 return self[-n:] 531 532 def reverse(self, lazy = False): 533 """ 534 >>> iTuple.range(3).reverse() 535 iTuple(2, 1, 0) 536 """ 537 if lazy: 538 return reversed(self) 539 return type(self)(data=reversed(self)) 540 541 def take_while(self, f, n = None, lazy = False): 542 """ 543 >>> iTuple.range(3).take_while(lambda v: v < 1) 544 iTuple(0) 545 """ 546 def iter(): 547 i = 0 548 for v in self: 549 if f(v) and (n is None or i < n): 550 yield v 551 i += 1 552 else: 553 return 554 res = iter() 555 return res if lazy else type(self)(data=res) 556 557 def tail_while(self, f, n = None): 558 """ 559 >>> iTuple.range(3).tail_while(lambda v: v > 1) 560 iTuple(2) 561 """ 562 i = 0 563 for v in reversed(self): 564 if f(v) and (n is None or i < n): 565 i += 1 566 else: 567 break 568 return self.tail(i) 569 570 # NOTE: from as in, starting from first true 571 # versus above, which is until first false 572 def take_after(self, f, n = None, lazy = False): 573 """ 574 >>> iTuple.range(3).take_after(lambda v: v < 1) 575 iTuple(1, 2) 576 >>> iTuple.range(3).take_after(lambda v: v < 1, n = 1) 577 iTuple(1) 578 """ 579 def iter(): 580 i = 0 581 for v in self: 582 if f(v): 583 pass 584 elif n is None or i < n: 585 yield v 586 i += 1 587 else: 588 return 589 res = iter() 590 return res if lazy else type(self)(data=res) 591 592 def tail_after(self, f, n = None): 593 """ 594 >>> iTuple.range(3).tail_after(lambda v: v < 2) 595 iTuple(0, 1) 596 >>> iTuple.range(3).tail_after(lambda v: v < 2, 1) 597 iTuple(1) 598 """ 599 l = 0 600 r = 0 601 for v in reversed(self): 602 if not f(v): 603 l += 1 604 elif n is None or r < n: 605 r += 1 606 else: 607 break 608 return self.tail(l + r).take(r) 609 610 def islice(self, left = None, right = None): 611 """ 612 >>> iTuple.range(5).islice(1, 3) 613 iTuple(1, 2) 614 """ 615 return self[left:right] 616 617 def unique(self): 618 """ 619 >>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique() 620 iTuple(1, 3, 2, 4) 621 """ 622 def iter(): 623 seen = set() 624 seen_add = seen.add 625 seen_contains = seen.__contains__ 626 for v in itertools.filterfalse(seen_contains, self): 627 seen_add(v) 628 yield v 629 return type(self)(data=iter()) 630 631 def sort(self, f = lambda v: v): 632 """ 633 >>> iTuple.range(3).reverse().sort() 634 iTuple(0, 1, 2) 635 >>> iTuple.range(3).sort() 636 iTuple(0, 1, 2) 637 """ 638 return type(self)(data=sorted(self, key = f)) 639 640 def accumulate(self, f, initial = None, lazy = False): 641 """ 642 >>> iTuple.range(3).accumulate(lambda acc, v: v) 643 iTuple(0, 1, 2) 644 >>> iTuple.range(3).accumulate(lambda acc, v: v, initial=0) 645 iTuple(0, 0, 1, 2) 646 >>> iTuple.range(3).accumulate(operator.add) 647 iTuple(0, 1, 3) 648 """ 649 res = itertools.accumulate(self, func=f, initial=initial) 650 return res if lazy else iTuple(data=res) 651 652 def foldcum(self, *args, **kwargs): 653 """ 654 >>> iTuple.range(3).foldcum(lambda acc, v: v) 655 iTuple(0, 1, 2) 656 >>> iTuple.range(3).foldcum(operator.add) 657 iTuple(0, 1, 3) 658 """ 659 return self.accumulate(*args, **kwargs) 660 661 def fold(self, f, initial=None): 662 """ 663 >>> iTuple.range(3).fold(lambda acc, v: v) 664 2 665 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 666 2 667 >>> iTuple.range(3).fold(operator.add) 668 3 669 """ 670 if initial is not None: 671 res = functools.reduce(f, self, initial) 672 else: 673 res = functools.reduce(f, self) 674 return res 675 676 # ----- 677 678 # combinatorics 679 680 # ----- 681 682# --------------------------------------------------------------- 683 684@nTuple.decorate 685class Example(typing.NamedTuple): 686 """ 687 >>> ex = Example(1, "a") 688 >>> ex 689 Example(x=1, s='a', it=iTuple()) 690 >>> ex.cls 691 <class 'xtuples.xtuples.nTuple'> 692 >>> ex.pipe(lambda nt: nt.x) 693 1 694 >>> f = ex.partial(lambda nt, v: nt.x * v) 695 >>> f(2) 696 2 697 >>> f(3) 698 3 699 """ 700 # NOTE: cls, pipe, partial are mandatory boilerplate 701 702 x: int 703 s: str 704 it: iTuple = iTuple([]) 705 706 @property 707 def cls(self): 708 ... 709 710 def pipe(self, f, *args, at = None, **kwargs): 711 ... 712 713 def partial(self, f, *args, at = None, **kwargs): 714 ... 715 716# --------------------------------------------------------------- 717 718# TODO: context manager to control 719# if we add the type information when writing to json or not 720 721# TODO: context mananger to control 722# lazy default behaviour (ie. default to lazy or not) 723 724__all__ = [ 725 "iTuple", 726 "nTuple", 727 "fDict", 728 "Example", 729] 730 731# ---------------------------------------------------------------
@dataclasses.dataclass(init=False, repr=True)
class
iTuple229@dataclasses.dataclass(init = False, repr=True) 230class iTuple(collections.UserList, tuple): # type: ignore 231 __slots__ = () 232 233 data: tuple # type: ignore 234 235 # ----- 236 237 @staticmethod 238 def __new__(cls, data = None): 239 # NOTE: we use cls not array 240 # so sub-classing *does* change identity 241 if isinstance(data, cls): 242 return data 243 return super().__new__(cls, data=data) 244 245 @staticmethod 246 def wrap_tuple(data): 247 return data if isinstance(data, tuple) else tuple(data) 248 249 def __init__(self, data = None): 250 # TODO: option for lazy init? 251 self.data = ( 252 tuple() if data is None 253 else self.wrap_tuple(data) 254 ) 255 256 def __repr__(self): 257 s = super().__repr__() 258 return "{}({})".format( 259 type(self).__name__, 260 s[1:-2 if s[-2] == "," else -1], 261 ) 262 263 def __hash__(self): 264 return hash(self.data) 265 266 @classmethod 267 def decorate(meta, cls): 268 assert cls.__name__ not in REGISTRY 269 REGISTRY[cls.__name__] = cls 270 return cls 271 272 # ----- 273 274 @classmethod 275 def range(cls, *args, **kwargs): 276 """ 277 >>> iTuple.range(3) 278 iTuple(0, 1, 2) 279 """ 280 return cls(range(*args, **kwargs)) 281 282 @classmethod 283 def from_keys(cls, d): 284 """ 285 >>> iTuple.from_keys({i: i + 1 for i in range(2)}) 286 iTuple(0, 1) 287 """ 288 return cls(d.keys()) 289 290 @classmethod 291 def from_values(cls, d): 292 """ 293 >>> iTuple.from_values({i: i + 1 for i in range(2)}) 294 iTuple(1, 2) 295 """ 296 return cls(d.values()) 297 298 @classmethod 299 def from_items(cls, d): 300 """ 301 >>> iTuple.from_items({i: i + 1 for i in range(2)}) 302 iTuple((0, 1), (1, 2)) 303 """ 304 return cls(d.items()) 305 306 # ----- 307 308 def pipe(self, f, *args, at = None, **kwargs): 309 """ 310 >>> iTuple.range(2).pipe(lambda it: it) 311 iTuple(0, 1) 312 >>> iTuple.range(2).pipe( 313 ... lambda it, v: it.map(lambda x: x * v), 2 314 ... ) 315 iTuple(0, 2) 316 """ 317 return pipe(f, self, *args, at = at, **kwargs) 318 319 def partial(self, f, *args, **kwargs): 320 """ 321 >>> f = iTuple.range(2).partial( 322 ... lambda it, v: it.map(lambda x: x * v) 323 ... ) 324 >>> f(2) 325 iTuple(0, 2) 326 >>> f(3) 327 iTuple(0, 3) 328 """ 329 return functools.partial(f, self, *args, **kwargs) 330 331 # ----- 332 333 def len(self): 334 """ 335 >>> iTuple.range(3).len() 336 3 337 """ 338 return len(self) 339 340 def append(self, value, *values): 341 """ 342 >>> iTuple.range(1).append(1) 343 iTuple(0, 1) 344 >>> iTuple.range(1).append(1, 2) 345 iTuple(0, 1, 2) 346 >>> iTuple.range(1).append(1, 2, 3) 347 iTuple(0, 1, 2, 3) 348 >>> iTuple.range(1).append(1, (2,)) 349 iTuple(0, 1, (2,)) 350 """ 351 return self + (value, *values) 352 353 def prepend(self, value, *values): 354 """ 355 >>> iTuple.range(1).prepend(1) 356 iTuple(1, 0) 357 >>> iTuple.range(1).prepend(1, 2) 358 iTuple(1, 2, 0) 359 >>> iTuple.range(1).prepend(1, 2, 3) 360 iTuple(1, 2, 3, 0) 361 >>> iTuple.range(1).prepend(1, (2,)) 362 iTuple(1, (2,), 0) 363 """ 364 return (value, *values) + self 365 366 def zip(self, *itrs, lazy = False): 367 """ 368 >>> iTuple([[1, 1], [2, 2], [3, 3]]).zip() 369 iTuple((1, 2, 3), (1, 2, 3)) 370 >>> iTuple([iTuple.range(3), iTuple.range(1, 4)]).zip() 371 iTuple((0, 1), (1, 2), (2, 3)) 372 >>> iTuple.range(3).zip(iTuple.range(1, 4)) 373 iTuple((0, 1), (1, 2), (2, 3)) 374 """ 375 if len(itrs) == 0: 376 res = zip(*self) 377 else: 378 res = zip(self, *itrs) 379 return res if lazy else iTuple(data=res) 380 381 def flatten(self): 382 """ 383 >>> iTuple.range(3).map(lambda x: [x]).flatten() 384 iTuple(0, 1, 2) 385 """ 386 return iTuple(itertools.chain(*self)) 387 388 def extend(self, value, *values): 389 """ 390 >>> iTuple.range(1).extend((1,)) 391 iTuple(0, 1) 392 >>> iTuple.range(1).extend([1]) 393 iTuple(0, 1) 394 >>> iTuple.range(1).extend([1], [2]) 395 iTuple(0, 1, 2) 396 >>> iTuple.range(1).extend([1], [[2]]) 397 iTuple(0, 1, [2]) 398 >>> iTuple.range(1).extend([1], [[2]], [2]) 399 iTuple(0, 1, [2], 2) 400 """ 401 return iTuple(itertools.chain.from_iterable( 402 (self, value, *values) 403 )) 404 405 def pretend(self, value, *values): 406 """ 407 >>> iTuple.range(1).pretend((1,)) 408 iTuple(1, 0) 409 >>> iTuple.range(1).pretend([1]) 410 iTuple(1, 0) 411 >>> iTuple.range(1).pretend([1], [2]) 412 iTuple(1, 2, 0) 413 >>> iTuple.range(1).pretend([1], [[2]]) 414 iTuple(1, [2], 0) 415 >>> iTuple.range(1).pretend([1], [[2]], [2]) 416 iTuple(1, [2], 2, 0) 417 """ 418 return iTuple(itertools.chain.from_iterable( 419 (value, *values, self) 420 )) 421 422 def filter_eq(self, v, f = None, eq = None, lazy = False): 423 """ 424 >>> iTuple.range(3).filter_eq(1) 425 iTuple(1) 426 """ 427 if f is None and eq is None: 428 res = filter(lambda x: x == v, self) 429 elif f is not None: 430 res = filter(lambda x: f(x) == v, self) 431 elif eq is not None: 432 res = filter(lambda x: eq(x, v), self) 433 else: 434 res = filter(lambda x: eq(f(x), v), self) 435 return res if lazy else type(self)(data=res) 436 437 def filter(self, f, eq = None, lazy = False): 438 """ 439 >>> iTuple.range(3).filter(lambda x: x > 1) 440 iTuple(2) 441 """ 442 return self.filter_eq(True, f = f, eq = eq, lazy = lazy) 443 444 def map(self, f, *iterables, lazy = False): 445 """ 446 >>> iTuple.range(3).map(lambda x: x * 2) 447 iTuple(0, 2, 4) 448 """ 449 res = map(f, self, *iterables) 450 return res if lazy else iTuple(data=res) 451 452 def enumerate(self): 453 """ 454 >>> iTuple.range(3).enumerate() 455 iTuple((0, 0), (1, 1), (2, 2)) 456 """ 457 # TODO: allow lazy 458 return iTuple(enumerate(self)) 459 460 def groupby( 461 self, 462 f, 463 lazy = False, 464 keys = False, 465 pipe= None, 466 ): 467 """ 468 >>> iTuple.range(3).groupby(lambda x: x < 2) 469 iTuple((0, 1), (2,)) 470 >>> iTuple.range(3).groupby( 471 ... lambda x: x < 2, keys=True, pipe=fDict 472 ... ) 473 {True: (0, 1), False: (2,)} 474 """ 475 # TODO: lazy no keys 476 res = itertools.groupby(self, key=f) 477 if lazy and keys and pipe is None: 478 return res 479 if pipe is None: 480 pipe = iTuple 481 if keys: 482 return pipe((k, tuple(g),) for k, g in res) 483 else: 484 return pipe(tuple(g) for k, g in res) 485 486 def first(self): 487 """ 488 >>> iTuple.range(3).first() 489 0 490 """ 491 return self[0] 492 493 def last(self): 494 """ 495 >>> iTuple.range(3).last() 496 2 497 """ 498 return self[-1] 499 500 def first_where(self, f): 501 """ 502 >>> iTuple.range(3).first_where(lambda v: v > 0) 503 1 504 """ 505 for v in self: 506 if f(v): 507 return v 508 return None 509 510 def last_where(self, f): 511 """ 512 >>> iTuple.range(3).last_where(lambda v: v < 2) 513 1 514 """ 515 for v in reversed(self): 516 if f(v): 517 return v 518 return None 519 520 def take(self, n): 521 """ 522 >>> iTuple.range(3).take(2) 523 iTuple(0, 1) 524 """ 525 return self[:n] 526 527 def tail(self, n): 528 """ 529 >>> iTuple.range(3).tail(2) 530 iTuple(1, 2) 531 """ 532 return self[-n:] 533 534 def reverse(self, lazy = False): 535 """ 536 >>> iTuple.range(3).reverse() 537 iTuple(2, 1, 0) 538 """ 539 if lazy: 540 return reversed(self) 541 return type(self)(data=reversed(self)) 542 543 def take_while(self, f, n = None, lazy = False): 544 """ 545 >>> iTuple.range(3).take_while(lambda v: v < 1) 546 iTuple(0) 547 """ 548 def iter(): 549 i = 0 550 for v in self: 551 if f(v) and (n is None or i < n): 552 yield v 553 i += 1 554 else: 555 return 556 res = iter() 557 return res if lazy else type(self)(data=res) 558 559 def tail_while(self, f, n = None): 560 """ 561 >>> iTuple.range(3).tail_while(lambda v: v > 1) 562 iTuple(2) 563 """ 564 i = 0 565 for v in reversed(self): 566 if f(v) and (n is None or i < n): 567 i += 1 568 else: 569 break 570 return self.tail(i) 571 572 # NOTE: from as in, starting from first true 573 # versus above, which is until first false 574 def take_after(self, f, n = None, lazy = False): 575 """ 576 >>> iTuple.range(3).take_after(lambda v: v < 1) 577 iTuple(1, 2) 578 >>> iTuple.range(3).take_after(lambda v: v < 1, n = 1) 579 iTuple(1) 580 """ 581 def iter(): 582 i = 0 583 for v in self: 584 if f(v): 585 pass 586 elif n is None or i < n: 587 yield v 588 i += 1 589 else: 590 return 591 res = iter() 592 return res if lazy else type(self)(data=res) 593 594 def tail_after(self, f, n = None): 595 """ 596 >>> iTuple.range(3).tail_after(lambda v: v < 2) 597 iTuple(0, 1) 598 >>> iTuple.range(3).tail_after(lambda v: v < 2, 1) 599 iTuple(1) 600 """ 601 l = 0 602 r = 0 603 for v in reversed(self): 604 if not f(v): 605 l += 1 606 elif n is None or r < n: 607 r += 1 608 else: 609 break 610 return self.tail(l + r).take(r) 611 612 def islice(self, left = None, right = None): 613 """ 614 >>> iTuple.range(5).islice(1, 3) 615 iTuple(1, 2) 616 """ 617 return self[left:right] 618 619 def unique(self): 620 """ 621 >>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique() 622 iTuple(1, 3, 2, 4) 623 """ 624 def iter(): 625 seen = set() 626 seen_add = seen.add 627 seen_contains = seen.__contains__ 628 for v in itertools.filterfalse(seen_contains, self): 629 seen_add(v) 630 yield v 631 return type(self)(data=iter()) 632 633 def sort(self, f = lambda v: v): 634 """ 635 >>> iTuple.range(3).reverse().sort() 636 iTuple(0, 1, 2) 637 >>> iTuple.range(3).sort() 638 iTuple(0, 1, 2) 639 """ 640 return type(self)(data=sorted(self, key = f)) 641 642 def accumulate(self, f, initial = None, lazy = False): 643 """ 644 >>> iTuple.range(3).accumulate(lambda acc, v: v) 645 iTuple(0, 1, 2) 646 >>> iTuple.range(3).accumulate(lambda acc, v: v, initial=0) 647 iTuple(0, 0, 1, 2) 648 >>> iTuple.range(3).accumulate(operator.add) 649 iTuple(0, 1, 3) 650 """ 651 res = itertools.accumulate(self, func=f, initial=initial) 652 return res if lazy else iTuple(data=res) 653 654 def foldcum(self, *args, **kwargs): 655 """ 656 >>> iTuple.range(3).foldcum(lambda acc, v: v) 657 iTuple(0, 1, 2) 658 >>> iTuple.range(3).foldcum(operator.add) 659 iTuple(0, 1, 3) 660 """ 661 return self.accumulate(*args, **kwargs) 662 663 def fold(self, f, initial=None): 664 """ 665 >>> iTuple.range(3).fold(lambda acc, v: v) 666 2 667 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 668 2 669 >>> iTuple.range(3).fold(operator.add) 670 3 671 """ 672 if initial is not None: 673 res = functools.reduce(f, self, initial) 674 else: 675 res = functools.reduce(f, self) 676 return res
@classmethod
def
range(cls, *args, **kwargs):
274 @classmethod 275 def range(cls, *args, **kwargs): 276 """ 277 >>> iTuple.range(3) 278 iTuple(0, 1, 2) 279 """ 280 return cls(range(*args, **kwargs))
>>> iTuple.range(3)
iTuple(0, 1, 2)
@classmethod
def
from_keys(cls, d):
282 @classmethod 283 def from_keys(cls, d): 284 """ 285 >>> iTuple.from_keys({i: i + 1 for i in range(2)}) 286 iTuple(0, 1) 287 """ 288 return cls(d.keys())
>>> iTuple.from_keys({i: i + 1 for i in range(2)})
iTuple(0, 1)
@classmethod
def
from_values(cls, d):
290 @classmethod 291 def from_values(cls, d): 292 """ 293 >>> iTuple.from_values({i: i + 1 for i in range(2)}) 294 iTuple(1, 2) 295 """ 296 return cls(d.values())
>>> iTuple.from_values({i: i + 1 for i in range(2)})
iTuple(1, 2)
@classmethod
def
from_items(cls, d):
298 @classmethod 299 def from_items(cls, d): 300 """ 301 >>> iTuple.from_items({i: i + 1 for i in range(2)}) 302 iTuple((0, 1), (1, 2)) 303 """ 304 return cls(d.items())
>>> iTuple.from_items({i: i + 1 for i in range(2)})
iTuple((0, 1), (1, 2))
def
pipe(self, f, *args, at=None, **kwargs):
308 def pipe(self, f, *args, at = None, **kwargs): 309 """ 310 >>> iTuple.range(2).pipe(lambda it: it) 311 iTuple(0, 1) 312 >>> iTuple.range(2).pipe( 313 ... lambda it, v: it.map(lambda x: x * v), 2 314 ... ) 315 iTuple(0, 2) 316 """ 317 return pipe(f, self, *args, at = at, **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)
def
partial(self, f, *args, **kwargs):
319 def partial(self, f, *args, **kwargs): 320 """ 321 >>> f = iTuple.range(2).partial( 322 ... lambda it, v: it.map(lambda x: x * v) 323 ... ) 324 >>> f(2) 325 iTuple(0, 2) 326 >>> f(3) 327 iTuple(0, 3) 328 """ 329 return functools.partial(f, self, *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)
def
append(self, value, *values):
340 def append(self, value, *values): 341 """ 342 >>> iTuple.range(1).append(1) 343 iTuple(0, 1) 344 >>> iTuple.range(1).append(1, 2) 345 iTuple(0, 1, 2) 346 >>> iTuple.range(1).append(1, 2, 3) 347 iTuple(0, 1, 2, 3) 348 >>> iTuple.range(1).append(1, (2,)) 349 iTuple(0, 1, (2,)) 350 """ 351 return self + (value, *values)
>>> 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,))
def
prepend(self, value, *values):
353 def prepend(self, value, *values): 354 """ 355 >>> iTuple.range(1).prepend(1) 356 iTuple(1, 0) 357 >>> iTuple.range(1).prepend(1, 2) 358 iTuple(1, 2, 0) 359 >>> iTuple.range(1).prepend(1, 2, 3) 360 iTuple(1, 2, 3, 0) 361 >>> iTuple.range(1).prepend(1, (2,)) 362 iTuple(1, (2,), 0) 363 """ 364 return (value, *values) + self
>>> 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)
def
zip(self, *itrs, lazy=False):
366 def zip(self, *itrs, lazy = False): 367 """ 368 >>> iTuple([[1, 1], [2, 2], [3, 3]]).zip() 369 iTuple((1, 2, 3), (1, 2, 3)) 370 >>> iTuple([iTuple.range(3), iTuple.range(1, 4)]).zip() 371 iTuple((0, 1), (1, 2), (2, 3)) 372 >>> iTuple.range(3).zip(iTuple.range(1, 4)) 373 iTuple((0, 1), (1, 2), (2, 3)) 374 """ 375 if len(itrs) == 0: 376 res = zip(*self) 377 else: 378 res = zip(self, *itrs) 379 return res if lazy else iTuple(data=res)
>>> 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))
def
flatten(self):
381 def flatten(self): 382 """ 383 >>> iTuple.range(3).map(lambda x: [x]).flatten() 384 iTuple(0, 1, 2) 385 """ 386 return iTuple(itertools.chain(*self))
>>> iTuple.range(3).map(lambda x: [x]).flatten()
iTuple(0, 1, 2)
def
extend(self, value, *values):
388 def extend(self, value, *values): 389 """ 390 >>> iTuple.range(1).extend((1,)) 391 iTuple(0, 1) 392 >>> iTuple.range(1).extend([1]) 393 iTuple(0, 1) 394 >>> iTuple.range(1).extend([1], [2]) 395 iTuple(0, 1, 2) 396 >>> iTuple.range(1).extend([1], [[2]]) 397 iTuple(0, 1, [2]) 398 >>> iTuple.range(1).extend([1], [[2]], [2]) 399 iTuple(0, 1, [2], 2) 400 """ 401 return iTuple(itertools.chain.from_iterable( 402 (self, value, *values) 403 ))
>>> 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)
def
pretend(self, value, *values):
405 def pretend(self, value, *values): 406 """ 407 >>> iTuple.range(1).pretend((1,)) 408 iTuple(1, 0) 409 >>> iTuple.range(1).pretend([1]) 410 iTuple(1, 0) 411 >>> iTuple.range(1).pretend([1], [2]) 412 iTuple(1, 2, 0) 413 >>> iTuple.range(1).pretend([1], [[2]]) 414 iTuple(1, [2], 0) 415 >>> iTuple.range(1).pretend([1], [[2]], [2]) 416 iTuple(1, [2], 2, 0) 417 """ 418 return iTuple(itertools.chain.from_iterable( 419 (value, *values, self) 420 ))
>>> 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)
def
filter_eq(self, v, f=None, eq=None, lazy=False):
422 def filter_eq(self, v, f = None, eq = None, lazy = False): 423 """ 424 >>> iTuple.range(3).filter_eq(1) 425 iTuple(1) 426 """ 427 if f is None and eq is None: 428 res = filter(lambda x: x == v, self) 429 elif f is not None: 430 res = filter(lambda x: f(x) == v, self) 431 elif eq is not None: 432 res = filter(lambda x: eq(x, v), self) 433 else: 434 res = filter(lambda x: eq(f(x), v), self) 435 return res if lazy else type(self)(data=res)
>>> iTuple.range(3).filter_eq(1)
iTuple(1)
def
filter(self, f, eq=None, lazy=False):
437 def filter(self, f, eq = None, lazy = False): 438 """ 439 >>> iTuple.range(3).filter(lambda x: x > 1) 440 iTuple(2) 441 """ 442 return self.filter_eq(True, f = f, eq = eq, lazy = lazy)
>>> iTuple.range(3).filter(lambda x: x > 1)
iTuple(2)
def
map(self, f, *iterables, lazy=False):
444 def map(self, f, *iterables, lazy = False): 445 """ 446 >>> iTuple.range(3).map(lambda x: x * 2) 447 iTuple(0, 2, 4) 448 """ 449 res = map(f, self, *iterables) 450 return res if lazy else iTuple(data=res)
>>> iTuple.range(3).map(lambda x: x * 2)
iTuple(0, 2, 4)
def
enumerate(self):
452 def enumerate(self): 453 """ 454 >>> iTuple.range(3).enumerate() 455 iTuple((0, 0), (1, 1), (2, 2)) 456 """ 457 # TODO: allow lazy 458 return iTuple(enumerate(self))
>>> iTuple.range(3).enumerate()
iTuple((0, 0), (1, 1), (2, 2))
def
groupby(self, f, lazy=False, keys=False, pipe=None):
460 def groupby( 461 self, 462 f, 463 lazy = False, 464 keys = False, 465 pipe= None, 466 ): 467 """ 468 >>> iTuple.range(3).groupby(lambda x: x < 2) 469 iTuple((0, 1), (2,)) 470 >>> iTuple.range(3).groupby( 471 ... lambda x: x < 2, keys=True, pipe=fDict 472 ... ) 473 {True: (0, 1), False: (2,)} 474 """ 475 # TODO: lazy no keys 476 res = itertools.groupby(self, key=f) 477 if lazy and keys and pipe is None: 478 return res 479 if pipe is None: 480 pipe = iTuple 481 if keys: 482 return pipe((k, tuple(g),) for k, g in res) 483 else: 484 return pipe(tuple(g) for k, g in res)
>>> iTuple.range(3).groupby(lambda x: x < 2)
iTuple((0, 1), (2,))
>>> iTuple.range(3).groupby(
... lambda x: x < 2, keys=True, pipe=fDict
... )
{True: (0, 1), False: (2,)}
def
first_where(self, f):
500 def first_where(self, f): 501 """ 502 >>> iTuple.range(3).first_where(lambda v: v > 0) 503 1 504 """ 505 for v in self: 506 if f(v): 507 return v 508 return None
>>> iTuple.range(3).first_where(lambda v: v > 0)
1
def
last_where(self, f):
510 def last_where(self, f): 511 """ 512 >>> iTuple.range(3).last_where(lambda v: v < 2) 513 1 514 """ 515 for v in reversed(self): 516 if f(v): 517 return v 518 return None
>>> iTuple.range(3).last_where(lambda v: v < 2)
1
def
take(self, n):
520 def take(self, n): 521 """ 522 >>> iTuple.range(3).take(2) 523 iTuple(0, 1) 524 """ 525 return self[:n]
>>> iTuple.range(3).take(2)
iTuple(0, 1)
def
tail(self, n):
527 def tail(self, n): 528 """ 529 >>> iTuple.range(3).tail(2) 530 iTuple(1, 2) 531 """ 532 return self[-n:]
>>> iTuple.range(3).tail(2)
iTuple(1, 2)
def
reverse(self, lazy=False):
534 def reverse(self, lazy = False): 535 """ 536 >>> iTuple.range(3).reverse() 537 iTuple(2, 1, 0) 538 """ 539 if lazy: 540 return reversed(self) 541 return type(self)(data=reversed(self))
>>> iTuple.range(3).reverse()
iTuple(2, 1, 0)
def
take_while(self, f, n=None, lazy=False):
543 def take_while(self, f, n = None, lazy = False): 544 """ 545 >>> iTuple.range(3).take_while(lambda v: v < 1) 546 iTuple(0) 547 """ 548 def iter(): 549 i = 0 550 for v in self: 551 if f(v) and (n is None or i < n): 552 yield v 553 i += 1 554 else: 555 return 556 res = iter() 557 return res if lazy else type(self)(data=res)
>>> iTuple.range(3).take_while(lambda v: v < 1)
iTuple(0)
def
tail_while(self, f, n=None):
559 def tail_while(self, f, n = None): 560 """ 561 >>> iTuple.range(3).tail_while(lambda v: v > 1) 562 iTuple(2) 563 """ 564 i = 0 565 for v in reversed(self): 566 if f(v) and (n is None or i < n): 567 i += 1 568 else: 569 break 570 return self.tail(i)
>>> iTuple.range(3).tail_while(lambda v: v > 1)
iTuple(2)
def
take_after(self, f, n=None, lazy=False):
574 def take_after(self, f, n = None, lazy = False): 575 """ 576 >>> iTuple.range(3).take_after(lambda v: v < 1) 577 iTuple(1, 2) 578 >>> iTuple.range(3).take_after(lambda v: v < 1, n = 1) 579 iTuple(1) 580 """ 581 def iter(): 582 i = 0 583 for v in self: 584 if f(v): 585 pass 586 elif n is None or i < n: 587 yield v 588 i += 1 589 else: 590 return 591 res = iter() 592 return res if lazy else type(self)(data=res)
>>> 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
tail_after(self, f, n=None):
594 def tail_after(self, f, n = None): 595 """ 596 >>> iTuple.range(3).tail_after(lambda v: v < 2) 597 iTuple(0, 1) 598 >>> iTuple.range(3).tail_after(lambda v: v < 2, 1) 599 iTuple(1) 600 """ 601 l = 0 602 r = 0 603 for v in reversed(self): 604 if not f(v): 605 l += 1 606 elif n is None or r < n: 607 r += 1 608 else: 609 break 610 return self.tail(l + r).take(r)
>>> iTuple.range(3).tail_after(lambda v: v < 2)
iTuple(0, 1)
>>> iTuple.range(3).tail_after(lambda v: v < 2, 1)
iTuple(1)
def
islice(self, left=None, right=None):
612 def islice(self, left = None, right = None): 613 """ 614 >>> iTuple.range(5).islice(1, 3) 615 iTuple(1, 2) 616 """ 617 return self[left:right]
>>> iTuple.range(5).islice(1, 3)
iTuple(1, 2)
def
unique(self):
619 def unique(self): 620 """ 621 >>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique() 622 iTuple(1, 3, 2, 4) 623 """ 624 def iter(): 625 seen = set() 626 seen_add = seen.add 627 seen_contains = seen.__contains__ 628 for v in itertools.filterfalse(seen_contains, self): 629 seen_add(v) 630 yield v 631 return type(self)(data=iter())
>>> iTuple([1, 1, 3, 2, 4, 2, 3]).unique()
iTuple(1, 3, 2, 4)
def
sort(self, f=<function iTuple.<lambda>>):
633 def sort(self, f = lambda v: v): 634 """ 635 >>> iTuple.range(3).reverse().sort() 636 iTuple(0, 1, 2) 637 >>> iTuple.range(3).sort() 638 iTuple(0, 1, 2) 639 """ 640 return type(self)(data=sorted(self, key = f))
>>> iTuple.range(3).reverse().sort()
iTuple(0, 1, 2)
>>> iTuple.range(3).sort()
iTuple(0, 1, 2)
def
accumulate(self, f, initial=None, lazy=False):
642 def accumulate(self, f, initial = None, lazy = False): 643 """ 644 >>> iTuple.range(3).accumulate(lambda acc, v: v) 645 iTuple(0, 1, 2) 646 >>> iTuple.range(3).accumulate(lambda acc, v: v, initial=0) 647 iTuple(0, 0, 1, 2) 648 >>> iTuple.range(3).accumulate(operator.add) 649 iTuple(0, 1, 3) 650 """ 651 res = itertools.accumulate(self, func=f, initial=initial) 652 return res if lazy else iTuple(data=res)
>>> 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)
def
foldcum(self, *args, **kwargs):
654 def foldcum(self, *args, **kwargs): 655 """ 656 >>> iTuple.range(3).foldcum(lambda acc, v: v) 657 iTuple(0, 1, 2) 658 >>> iTuple.range(3).foldcum(operator.add) 659 iTuple(0, 1, 3) 660 """ 661 return self.accumulate(*args, **kwargs)
>>> iTuple.range(3).foldcum(lambda acc, v: v)
iTuple(0, 1, 2)
>>> iTuple.range(3).foldcum(operator.add)
iTuple(0, 1, 3)
def
fold(self, f, initial=None):
663 def fold(self, f, initial=None): 664 """ 665 >>> iTuple.range(3).fold(lambda acc, v: v) 666 2 667 >>> iTuple.range(3).fold(lambda acc, v: v, initial=0) 668 2 669 >>> iTuple.range(3).fold(operator.add) 670 3 671 """ 672 if initial is not None: 673 res = functools.reduce(f, self, initial) 674 else: 675 res = functools.reduce(f, self) 676 return res
>>> 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
Inherited Members
- collections.UserList
- insert
- pop
- remove
- clear
- copy
- count
- index
class
nTuple(abc.ABC):
44class nTuple(abc.ABC): 45 46 @abc.abstractmethod 47 def __abstract__(self): 48 # NOTE: here to prevent initialise instances of this 49 # but rather use the decorator and typing.NamedTuple 50 return 51 52 @staticmethod 53 def pipe(obj, f, *args, at = None, **kwargs): 54 """ 55 >>> example = Example(1, "a") 56 >>> example.pipe(lambda a, b: a, None) 57 Example(x=1, s='a', it=iTuple()) 58 >>> example.pipe(lambda a, b: a, None, at = 1) 59 >>> example.pipe(lambda a, b: a, None, at = 'b') 60 >>> example.pipe(lambda a, b: a, a=None, at = 'b') 61 >>> example.pipe(lambda a, b: a, b=None, at = 'a') 62 Example(x=1, s='a', it=iTuple()) 63 >>> example.pipe(lambda a, b: a, None, at = 0) 64 Example(x=1, s='a', it=iTuple()) 65 """ 66 return pipe(f, obj, *args, at = at, **kwargs) 67 68 @staticmethod 69 def partial(obj, f, *args, **kwargs): 70 return functools.partial(f, obj, *args, **kwargs) 71 72 @classmethod 73 def is_subclass(cls, t): 74 """ 75 >>> nTuple.is_subclass(tuple) 76 False 77 >>> nTuple.is_subclass(Example(1, "a")) 78 False 79 >>> nTuple.is_subclass(Example) 80 True 81 """ 82 try: 83 is_sub = issubclass(t, tuple) 84 except: 85 is_sub = False 86 return ( 87 is_sub and 88 hasattr(t, "cls") and 89 hasattr(t, "pipe") and 90 hasattr(t, "partial") 91 ) 92 93 @classmethod 94 def is_instance(cls, obj): 95 """ 96 >>> nTuple.is_instance(tuple) 97 False 98 >>> nTuple.is_instance(Example) 99 False 100 >>> nTuple.is_instance(Example(1, "a")) 101 True 102 """ 103 return ( 104 cls.is_subclass(type(obj)) and 105 hasattr(obj, '_asdict') and 106 hasattr(obj, '_fields') 107 ) 108 109 110 @staticmethod 111 def annotations(obj): 112 """ 113 >>> ex = Example(1, "a") 114 >>> ex.pipe(ex.cls.annotations) 115 {'x': <class 'int'>, 's': <class 'str'>, 'it': <class 'xtuples.xtuples.iTuple'>} 116 """ 117 return fDict(obj.__annotations__) 118 119 @classmethod 120 def as_dict(cls, obj): 121 """ 122 >>> ex = Example(1, "a") 123 >>> ex.pipe(ex.cls.as_dict) 124 {'x': 1, 's': 'a', 'it': iTuple()} 125 """ 126 return fDict(obj._asdict()) 127 128 @classmethod 129 def decorate(meta, cls): 130 assert cls.__name__ not in REGISTRY 131 cls.pipe = meta.pipe 132 cls.partial = meta.partial 133 cls.cls = meta 134 REGISTRY[cls.__name__] = cls 135 return cls
Helper class that provides a standard way to create an ABC using inheritance.
@staticmethod
def
pipe(obj, f, *args, at=None, **kwargs):
52 @staticmethod 53 def pipe(obj, f, *args, at = None, **kwargs): 54 """ 55 >>> example = Example(1, "a") 56 >>> example.pipe(lambda a, b: a, None) 57 Example(x=1, s='a', it=iTuple()) 58 >>> example.pipe(lambda a, b: a, None, at = 1) 59 >>> example.pipe(lambda a, b: a, None, at = 'b') 60 >>> example.pipe(lambda a, b: a, a=None, at = 'b') 61 >>> example.pipe(lambda a, b: a, b=None, at = 'a') 62 Example(x=1, s='a', it=iTuple()) 63 >>> example.pipe(lambda a, b: a, None, at = 0) 64 Example(x=1, s='a', it=iTuple()) 65 """ 66 return pipe(f, obj, *args, at = at, **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())
@classmethod
def
is_subclass(cls, t):
72 @classmethod 73 def is_subclass(cls, t): 74 """ 75 >>> nTuple.is_subclass(tuple) 76 False 77 >>> nTuple.is_subclass(Example(1, "a")) 78 False 79 >>> nTuple.is_subclass(Example) 80 True 81 """ 82 try: 83 is_sub = issubclass(t, tuple) 84 except: 85 is_sub = False 86 return ( 87 is_sub and 88 hasattr(t, "cls") and 89 hasattr(t, "pipe") and 90 hasattr(t, "partial") 91 )
>>> nTuple.is_subclass(tuple)
False
>>> nTuple.is_subclass(Example(1, "a"))
False
>>> nTuple.is_subclass(Example)
True
@classmethod
def
is_instance(cls, obj):
93 @classmethod 94 def is_instance(cls, obj): 95 """ 96 >>> nTuple.is_instance(tuple) 97 False 98 >>> nTuple.is_instance(Example) 99 False 100 >>> nTuple.is_instance(Example(1, "a")) 101 True 102 """ 103 return ( 104 cls.is_subclass(type(obj)) and 105 hasattr(obj, '_asdict') and 106 hasattr(obj, '_fields') 107 )
>>> nTuple.is_instance(tuple)
False
>>> nTuple.is_instance(Example)
False
>>> nTuple.is_instance(Example(1, "a"))
True
@staticmethod
def
annotations(obj):
110 @staticmethod 111 def annotations(obj): 112 """ 113 >>> ex = Example(1, "a") 114 >>> ex.pipe(ex.cls.annotations) 115 {'x': <class 'int'>, 's': <class 'str'>, 'it': <class 'xtuples.xtuples.iTuple'>} 116 """ 117 return fDict(obj.__annotations__)
>>> ex = Example(1, "a")
>>> ex.pipe(ex.cls.annotations)
{'x': <class 'int'>, 's': <class 'str'>, 'it': <class 'xtuples.xtuples.iTuple'>}
@classmethod
def
as_dict(cls, obj):
119 @classmethod 120 def as_dict(cls, obj): 121 """ 122 >>> ex = Example(1, "a") 123 >>> ex.pipe(ex.cls.as_dict) 124 {'x': 1, 's': 'a', 'it': iTuple()} 125 """ 126 return fDict(obj._asdict())
>>> ex = Example(1, "a")
>>> ex.pipe(ex.cls.as_dict)
{'x': 1, 's': 'a', 'it': iTuple()}
class
fDict(collections.UserDict):
139class fDict(collections.UserDict): 140 __slots__ = () 141 142 data: dict 143 144 def pipe(self, f, *args, at=None, **kwargs): 145 """ 146 >>> fDict({0: 1}).pipe(lambda d: d.map_values( 147 ... lambda v: v + 1 148 ... )) 149 {0: 2} 150 """ 151 res = pipe(f, self, *args, at = at, **kwargs) 152 if isinstance(res, dict): 153 return fDict(res) 154 return res 155 156 def partial(self, f, *args, **kwargs): 157 """ 158 >>> f = fDict({0: 1}).partial( 159 ... lambda d, n: d.map_values(lambda v: v + n) 160 ... ) 161 >>> f(1) 162 {0: 2} 163 >>> f(2) 164 {0: 3} 165 """ 166 return functools.partial(f, self, *args, **kwargs) 167 168 def keys_tuple(self): 169 """ 170 >>> fDict({0: 1}).keys_tuple() 171 iTuple(0) 172 """ 173 return iTuple.from_keys(self) 174 175 def values_tuple(self): 176 """ 177 >>> fDict({0: 1}).values_tuple() 178 iTuple(1) 179 """ 180 return iTuple.from_values(self) 181 182 def items_tuple(self): 183 """ 184 >>> fDict({0: 1}).items_tuple() 185 iTuple((0, 1)) 186 """ 187 return iTuple.from_items(self) 188 189 # NOTE: we have separate map implementations 190 # as they are constant size, dict to dict 191 # other iterator functions should use iTuple (from the above) 192 193 def map_keys(self, f, *args, **kwargs): 194 """ 195 >>> fDict({0: 1}).map_keys(lambda v: v + 1) 196 {1: 1} 197 """ 198 return fDict( 199 (f(k, *args, **kwargs), v) for k, v in self.items() 200 ) 201 202 def map_values(self, f, *args, **kwargs): 203 """ 204 >>> fDict({0: 1}).map_values(lambda v: v + 1) 205 {0: 2} 206 """ 207 return fDict( 208 (k, f(v, *args, **kwargs)) for k, v in self.items() 209 ) 210 211 def map_items(self, f, *args, **kwargs): 212 """ 213 >>> fDict({0: 1}).map_items(lambda k, v: (v, k)) 214 {1: 0} 215 """ 216 return fDict( 217 f(k, v, *args, **kwargs) for k, v in self.items() 218 ) 219 220 def invert(self): 221 """ 222 >>> fDict({0: 1}).invert() 223 {1: 0} 224 """ 225 return fDict((v, k) for k, v in self.items())
def
pipe(self, f, *args, at=None, **kwargs):
144 def pipe(self, f, *args, at=None, **kwargs): 145 """ 146 >>> fDict({0: 1}).pipe(lambda d: d.map_values( 147 ... lambda v: v + 1 148 ... )) 149 {0: 2} 150 """ 151 res = pipe(f, self, *args, at = at, **kwargs) 152 if isinstance(res, dict): 153 return fDict(res) 154 return res
>>> fDict({0: 1}).pipe(lambda d: d.map_values(
... lambda v: v + 1
... ))
{0: 2}
def
partial(self, f, *args, **kwargs):
156 def partial(self, f, *args, **kwargs): 157 """ 158 >>> f = fDict({0: 1}).partial( 159 ... lambda d, n: d.map_values(lambda v: v + n) 160 ... ) 161 >>> f(1) 162 {0: 2} 163 >>> f(2) 164 {0: 3} 165 """ 166 return functools.partial(f, self, *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}
def
keys_tuple(self):
168 def keys_tuple(self): 169 """ 170 >>> fDict({0: 1}).keys_tuple() 171 iTuple(0) 172 """ 173 return iTuple.from_keys(self)
>>> fDict({0: 1}).keys_tuple()
iTuple(0)
def
values_tuple(self):
175 def values_tuple(self): 176 """ 177 >>> fDict({0: 1}).values_tuple() 178 iTuple(1) 179 """ 180 return iTuple.from_values(self)
>>> fDict({0: 1}).values_tuple()
iTuple(1)
def
items_tuple(self):
182 def items_tuple(self): 183 """ 184 >>> fDict({0: 1}).items_tuple() 185 iTuple((0, 1)) 186 """ 187 return iTuple.from_items(self)
>>> fDict({0: 1}).items_tuple()
iTuple((0, 1))
def
map_keys(self, f, *args, **kwargs):
193 def map_keys(self, f, *args, **kwargs): 194 """ 195 >>> fDict({0: 1}).map_keys(lambda v: v + 1) 196 {1: 1} 197 """ 198 return fDict( 199 (f(k, *args, **kwargs), v) for k, v in self.items() 200 )
>>> fDict({0: 1}).map_keys(lambda v: v + 1)
{1: 1}
def
map_values(self, f, *args, **kwargs):
202 def map_values(self, f, *args, **kwargs): 203 """ 204 >>> fDict({0: 1}).map_values(lambda v: v + 1) 205 {0: 2} 206 """ 207 return fDict( 208 (k, f(v, *args, **kwargs)) for k, v in self.items() 209 )
>>> fDict({0: 1}).map_values(lambda v: v + 1)
{0: 2}
def
map_items(self, f, *args, **kwargs):
211 def map_items(self, f, *args, **kwargs): 212 """ 213 >>> fDict({0: 1}).map_items(lambda k, v: (v, k)) 214 {1: 0} 215 """ 216 return fDict( 217 f(k, v, *args, **kwargs) for k, v in self.items() 218 )
>>> fDict({0: 1}).map_items(lambda k, v: (v, k))
{1: 0}
def
invert(self):
220 def invert(self): 221 """ 222 >>> fDict({0: 1}).invert() 223 {1: 0} 224 """ 225 return fDict((v, k) for k, v in self.items())
>>> fDict({0: 1}).invert()
{1: 0}
Inherited Members
- collections.UserDict
- UserDict
- copy
- fromkeys
- collections.abc.MutableMapping
- pop
- popitem
- clear
- update
- setdefault
- collections.abc.Mapping
- get
- keys
- items
- values
@nTuple.decorate
class
Example686@nTuple.decorate 687class Example(typing.NamedTuple): 688 """ 689 >>> ex = Example(1, "a") 690 >>> ex 691 Example(x=1, s='a', it=iTuple()) 692 >>> ex.cls 693 <class 'xtuples.xtuples.nTuple'> 694 >>> ex.pipe(lambda nt: nt.x) 695 1 696 >>> f = ex.partial(lambda nt, v: nt.x * v) 697 >>> f(2) 698 2 699 >>> f(3) 700 3 701 """ 702 # NOTE: cls, pipe, partial are mandatory boilerplate 703 704 x: int 705 s: str 706 it: iTuple = iTuple([]) 707 708 @property 709 def cls(self): 710 ... 711 712 def pipe(self, f, *args, at = None, **kwargs): 713 ... 714 715 def partial(self, f, *args, at = None, **kwargs): 716 ...
>>> 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
Example(x: int, s: str, it: xtuples.xtuples.iTuple = iTuple())
Create new instance of Example(x, s, it)
@staticmethod
def
pipe(obj, f, *args, at=None, **kwargs):
52 @staticmethod 53 def pipe(obj, f, *args, at = None, **kwargs): 54 """ 55 >>> example = Example(1, "a") 56 >>> example.pipe(lambda a, b: a, None) 57 Example(x=1, s='a', it=iTuple()) 58 >>> example.pipe(lambda a, b: a, None, at = 1) 59 >>> example.pipe(lambda a, b: a, None, at = 'b') 60 >>> example.pipe(lambda a, b: a, a=None, at = 'b') 61 >>> example.pipe(lambda a, b: a, b=None, at = 'a') 62 Example(x=1, s='a', it=iTuple()) 63 >>> example.pipe(lambda a, b: a, None, at = 0) 64 Example(x=1, s='a', it=iTuple()) 65 """ 66 return pipe(f, obj, *args, at = at, **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())
Inherited Members
- builtins.tuple
- index
- count