sain.iter
Composable external iteration. See Iterator for more details.
1# BSD 3-Clause License 2# 3# Copyright (c) 2022-Present, nxtlo 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, this 10# list of conditions and the following disclaimer. 11# 12# * Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# * Neither the name of the copyright holder nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30"""Composable external iteration. See `Iterator` for more details.""" 31 32from __future__ import annotations 33 34__all__ = ( 35 # Core 36 "Iter", 37 "Iterator", 38 "TrustedIter", 39 # Adapters 40 "Cloned", 41 "Copied", 42 "Take", 43 "Filter", 44 "Map", 45 "Skip", 46 "Enumerate", 47 "TakeWhile", 48 "DropWhile", 49 "Chunks", 50 "Empty", 51 "Repeat", 52 "Once", 53 "ExactSizeIterator", 54 # Functions 55 "into_iter", 56 "empty", 57 "once", 58 "repeat", 59) 60 61import abc 62import collections.abc as collections 63import copy 64import itertools 65import typing 66 67from . import default as _default 68from . import futures 69from . import option as _option 70from . import result as _result 71from .macros import rustc_diagnostic_item 72from .macros import unsafe 73 74Item = typing.TypeVar("Item") 75"""The type of the item that is being yielded.""" 76 77OtherItem = typing.TypeVar("OtherItem") 78"""The type of the item that is being mapped into then yielded.""" 79 80AnyIter = typing.TypeVar("AnyIter", bound="Iterator[typing.Any]") 81 82 83if typing.TYPE_CHECKING: 84 import _typeshed 85 86 from .collections.slice import Slice 87 from .collections.vec import Vec 88 from .option import Option 89 90 Collector = ( 91 collections.MutableSequence[Item] 92 | set[Item] 93 | collections.MutableMapping[int, Item] 94 ) 95 _typeshed.ConvertibleToInt 96 Sum: typing.TypeAlias = ( 97 "Iterator[str]" 98 | "Iterator[typing.SupportsInt]" 99 | "Iterator[typing.SupportsIndex]" 100 | "Iterator[_typeshed.ReadableBuffer]" 101 | "Iterator[_typeshed.SupportsTrunc]" 102 | "Iterator[float]" 103 ) 104 105 106def unreachable() -> typing.NoReturn: 107 raise StopIteration( 108 "No more items exist in this iterator. It has been exhausted." 109 ) from None 110 111 112def oob() -> typing.NoReturn: 113 raise IndexError("index is out of bounds.") 114 115 116def diagnostic(cls: type[AnyIter]) -> type[AnyIter]: 117 def _repr(self: Iterator[typing.Any]) -> str: 118 return f"{type(self).__name__}(source: Iter<{type(self._it).__name__}>)" # pyright: ignore 119 120 cls.__repr__ = _repr 121 return cls 122 123 124@rustc_diagnostic_item("Iterator") 125class Iterator( 126 typing.Generic[Item], 127 abc.ABC, 128 _default.Default["Empty[Item]"], 129): 130 """An abstract interface for dealing with iterators. 131 132 This is exactly the same trait as `core::iter::Iterator` trait from Rust. 133 134 This is the main interface that any type can implement by basically inheriting from it. 135 The method `__next__` is the only method that needs to be implemented, You get all the other methods for free. 136 137 If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementers 138 and type hints. 139 140 Example 141 ------- 142 ```py 143 @dataclass 144 class Counter(Iterator[int]): 145 start: int = 0 146 stop: int | None = None 147 148 # implement the required method. 149 def __next__(self) -> int: 150 result = self.start 151 self.start += 1 152 153 if self.stop is not None and result >= self.stop: 154 raise StopIteration 155 156 return result 157 158 counter = Counter(start=0, stop=10) 159 for i in counter.map(lambda x: x * 2): # multiply each number 160 ... 161 ``` 162 """ 163 164 __slots__ = () 165 166 @abc.abstractmethod 167 def __next__(self) -> Item: 168 raise NotImplementedError 169 170 ################### 171 # const functions # 172 ################### 173 174 @staticmethod 175 @typing.final 176 def default() -> Empty[Item]: 177 """Return the default iterator for this type. It returns an empty iterator. 178 179 Example 180 ------- 181 ```py 182 it: Iterator[int] = Iter.default() 183 assert t.next().is_none() 184 ``` 185 """ 186 return Empty() 187 188 @typing.overload 189 def collect(self) -> collections.MutableSequence[Item]: ... 190 191 @typing.overload 192 def collect( 193 self, *, cast: collections.Callable[[Item], OtherItem] 194 ) -> collections.MutableSequence[OtherItem]: ... 195 196 @typing.final 197 def collect( 198 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 199 ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]: 200 """Collects all items in the iterator into a sequence. 201 202 Example 203 ------- 204 ```py 205 iterator = Iter(range(3)) 206 iterator.collect() 207 # (0, 1, 2, 3) 208 iterator.collect(cast=str) # Map each element and collect it. 209 # ('0', '1', '2', '3') 210 ``` 211 212 Parameters 213 ---------- 214 cast: `T | None` 215 An optional type to cast the items into. 216 If not provided the items will be returned as it's original type. 217 """ 218 if cast is not None: 219 return list(cast(i) for i in self) 220 221 return list(_ for _ in self) 222 223 @typing.final 224 def collect_into(self, collection: Collector[Item]) -> None: 225 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 226 227 Example 228 ------- 229 ```py 230 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 231 uniques = set() 232 iterator.collect_into(uniques) 233 # assert uniques == {1, 2, 3, 4, 6} 234 ``` 235 236 Parameters 237 ---------- 238 collection: `MutableSequence[T]` | `set[T]` 239 The collection to extend the items in this iterator with. 240 """ 241 if isinstance(collection, collections.MutableSequence): 242 collection.extend(_ for _ in self) 243 elif isinstance(collection, collections.MutableSet): 244 collection.update(_ for _ in self) 245 else: 246 for idx, item in enumerate(self): 247 collection[idx] = item 248 249 @typing.final 250 def to_vec(self) -> Vec[Item]: 251 """Convert this iterator into `Vec[T]`. 252 253 Example 254 ------- 255 ```py 256 it = sain.iter.once(0) 257 vc = it.to_vec() 258 259 assert to_vec == [0] 260 ``` 261 """ 262 from .collections.vec import Vec 263 264 return Vec(_ for _ in self) 265 266 @typing.final 267 def sink(self) -> None: 268 """Consume all elements from this iterator, flushing it into the sink. 269 270 Example 271 ------- 272 ```py 273 it = Iter((1, 2, 3)) 274 it.sink() 275 assert it.next().is_none() 276 ``` 277 """ 278 for _ in self: 279 pass 280 281 @typing.final 282 def raw_parts(self) -> collections.Generator[Item, None, None]: 283 """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items. 284 285 This mainly used for objects that needs to satisfy its exact type. 286 287 ```py 288 it = Iter("cba") 289 sort = sorted(it.raw_parts()) 290 291 assert it.count() == 0 292 assert sort == ["a", "b", "c"] 293 ``` 294 """ 295 for item in self: 296 yield item 297 298 ################## 299 # default impl's # 300 ################## 301 302 def next(self) -> Option[Item]: 303 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 304 305 Example 306 ------- 307 ```py 308 iterator = Iter(["1", "2"]) 309 assert iterator.next() == Some("1") 310 assert iterator.next() == Some("2") 311 assert iterator.next().is_none() 312 ``` 313 """ 314 try: 315 return _option.Some(self.__next__()) 316 except StopIteration: 317 # ! SAFETY: No more items in the iterator. 318 return _option.NOTHING 319 320 def cloned(self) -> Cloned[Item]: 321 """Creates an iterator which shallow copies its elements by reference. 322 323 If you need a copy of the actual iterator and not the elements. 324 use `Iter.clone()` 325 326 .. note:: 327 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 328 on each item that is being yielded. 329 330 Example 331 ------- 332 ```py 333 @dataclass 334 class User: 335 users_ids: list[int] = [] 336 337 # An iterator which elements points to the same user. 338 user = User() 339 it = Iter((user, user)) 340 341 for u in it.cloned(): 342 u.user_ids.append(1) 343 344 # We iterated over the same user pointer twice and appended "1" 345 # since `copy` returns a shallow copy of nested structures. 346 assert len(user.user_ids) == 2 347 ``` 348 """ 349 return Cloned(self) 350 351 def copied(self) -> Copied[Item]: 352 """Creates an iterator which copies all of its elements by value. 353 354 If you only need a copy of the item reference, Use `.cloned()` instead. 355 356 .. note:: 357 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 358 on each item that is being yielded. 359 360 Example 361 ------- 362 ```py 363 @dataclass 364 class User: 365 users_ids: list[int] = [] 366 367 # An iterator which elements points to the same user. 368 user = User() 369 it = Iter((user, user)) 370 371 for u in it.copied(): 372 # A new list is created for each item. 373 u.user_ids.append(1) 374 375 # The actual list is untouched since we consumed a deep copy of it. 376 assert len(user.user_ids) == 0 377 ``` 378 """ 379 return Copied(self) 380 381 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 382 """Maps each item in the iterator to another type. 383 384 Example 385 ------- 386 ```py 387 iterator = Iter(["1", "2", "3"]).map(int) 388 389 for item in iterator: 390 assert isinstance(item, int) 391 ``` 392 393 Parameters 394 ---------- 395 predicate: `Callable[[Item], OtherItem]` 396 The function to map each item in the iterator to the other type. 397 """ 398 return Map(self, fn) 399 400 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 401 """Filters the iterator to only yield items that match the predicate. 402 403 Example 404 ------- 405 ```py 406 places = Iter(['London', 'Paris', 'Los Angeles']) 407 for place in places.filter(lambda place: place.startswith('L')): 408 print(place) 409 410 # London 411 # Los Angeles 412 ``` 413 """ 414 return Filter(self, predicate) 415 416 def take(self, count: int) -> Take[Item]: 417 """Take the first number of items until the number of items 418 are yielded or the end of the iterator is exhausted. 419 420 Example 421 ------- 422 ```py 423 iterator = Iter(['c', 'x', 'y']) 424 425 for x in iterator.take(2): 426 assert x in ('c', 'x') 427 428 # <Iter(['c', 'x'])> 429 ``` 430 """ 431 return Take(self, count) 432 433 def skip(self, count: int) -> Skip[Item]: 434 """Skips the first number of items in the iterator. 435 436 Example 437 ------- 438 ```py 439 iterator = Iter((1, 2, 3, 4)) 440 for i in iterator.skip(2): 441 print(i) 442 443 # 3 444 # 4 445 ``` 446 """ 447 return Skip(self, count) 448 449 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 450 """Create a new iterator that yields a tuple of the index and item. 451 452 Example 453 ------- 454 ```py 455 iterator = Iter([1, 2, 3]) 456 for index, item in iterator.enumerate(): 457 print(index, item) 458 459 # 0 1 460 # 1 2 461 # 2 3 462 ``` 463 """ 464 return Enumerate(self, start) 465 466 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 467 """yields items from the iterator while predicate returns `True`. 468 469 The rest of the items are discarded as soon as the predicate returns `False` 470 471 Example 472 ------- 473 ```py 474 iterator = Iter(['a', 'ab', 'xd', 'ba']) 475 for x in iterator.take_while(lambda x: 'a' in x): 476 print(x) 477 478 # a 479 # ab 480 ``` 481 482 Parameters 483 ---------- 484 predicate: `collections.Callable[[Item], bool]` 485 The function to predicate each item in the iterator. 486 """ 487 return TakeWhile(self, f) 488 489 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 490 """Yields items from the iterator while predicate returns `False`. 491 492 Example 493 ------- 494 ```py 495 iterator = Iter(['a', 'ab', 'xd', 'ba']) 496 for x in iterator.drop_while(lambda x: 'a' in x): 497 print(x) 498 499 # xd 500 # ba 501 ``` 502 503 Parameters 504 ---------- 505 predicate: `collections.Callable[[Item], bool]` 506 The function to predicate each item in the iterator. 507 """ 508 return DropWhile(self, f) 509 510 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 511 """Returns an iterator over `chunk_size` elements of the iterator at a time, 512 starting at the beginning of the iterator. 513 514 Example 515 ------- 516 ```py 517 iter = Iter(['a', 'b', 'c', 'd', 'e']) 518 chunks = iter.chunks() 519 assert chunks.next().unwrap() == ['a', 'b'] 520 assert chunks.next().unwrap() == ['c', 'd'] 521 assert chunks.next().unwrap() == ['e'] 522 assert chunks.next().is_none() 523 ``` 524 """ 525 return Chunks(self, chunk_size) 526 527 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 528 """Return `True` if all items in the iterator match the predicate. 529 530 Example 531 ------- 532 ```py 533 iterator = Iter([1, 2, 3]) 534 while iterator.all(lambda item: isinstance(item, int)): 535 print("Still all integers") 536 continue 537 # Still all integers 538 ``` 539 540 Parameters 541 ---------- 542 predicate: `collections.Callable[[Item], bool]` 543 The function to test each item in the iterator. 544 """ 545 return all(predicate(item) for item in self) 546 547 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 548 """`True` if any items in the iterator match the predicate. 549 550 Example 551 ------- 552 ```py 553 iterator = Iter([1, 2, 3]) 554 if iterator.any(lambda item: isinstance(item, int)): 555 print("At least one item is an int.") 556 # At least one item is an int. 557 ``` 558 559 Parameters 560 ---------- 561 predicate: `collections.Callable[[Item], bool]` 562 The function to test each item in the iterator. 563 """ 564 return any(predicate(item) for item in self) 565 566 def zip( 567 self, other: collections.Iterable[OtherItem] 568 ) -> Iter[tuple[Item, OtherItem]]: 569 """Zips the iterator with another iterable. 570 571 Example 572 ------- 573 ```py 574 iterator = Iter([1, 2, 3]) 575 for item, other_item in iterator.zip([4, 5, 6]): 576 assert item == other_item 577 <Iter([(1, 4), (2, 5), (3, 6)])> 578 ``` 579 580 Parameters 581 ---------- 582 other: `Iter[OtherItem]` 583 The iterable to zip with. 584 585 Returns 586 ------- 587 `Iter[tuple[Item, OtherItem]]` 588 The zipped iterator. 589 590 """ 591 return Iter(zip(self.raw_parts(), other)) 592 593 def sort( 594 self, 595 *, 596 key: collections.Callable[[Item], _typeshed.SupportsRichComparison], 597 reverse: bool = False, 598 ) -> Iter[Item]: 599 """Sorts the iterator. 600 601 Example 602 ------- 603 ```py 604 iterator = Iter([3, 1, 6, 7]) 605 for item in iterator.sort(key=lambda item: item < 3): 606 print(item) 607 # 1 608 # 3 609 # 6 610 # 7 611 ``` 612 613 Parameters 614 ---------- 615 key: `collections.Callable[[Item], Any]` 616 The function to sort by. 617 reverse: `bool` 618 Whether to reverse the sort. 619 """ 620 return Iter(sorted(self.raw_parts(), key=key, reverse=reverse)) 621 622 def reversed(self) -> Iter[Item]: 623 """Returns a new iterator that yields the items in the iterator in reverse order. 624 625 This consumes this iterator into a sequence and return a new iterator containing all of the elements 626 in reversed order. 627 628 Example 629 ------- 630 ```py 631 iterator = Iter([3, 1, 6, 7]) 632 for item in iterator.reversed(): 633 print(item) 634 # 7 635 # 6 636 # 1 637 # 3 638 ``` 639 """ 640 # NOTE: In order to reverse the iterator we need to 641 # first collect it into some collection. 642 return Iter(reversed(list(_ for _ in self))) 643 644 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 645 """Returns a new iterator that yields all items from both iterators. 646 647 Example 648 ------- 649 ```py 650 iterator = Iter([1, 2, 3]) 651 other = [4, 5, 6] 652 653 for item in iterator.union(other): 654 print(item) 655 # 1 656 # 2 657 # 3 658 # 4 659 # 5 660 # 6 661 ``` 662 663 Parameters 664 ---------- 665 other: `Iter[Item]` 666 The iterable to union with. 667 """ 668 return Iter(itertools.chain(self.raw_parts(), other)) 669 670 def first(self) -> Option[Item]: 671 """Returns the first item in the iterator. 672 673 Example 674 ------- 675 ```py 676 iterator = Iter([3, 1, 6, 7]) 677 iterator.first().is_some_and(lambda x: x == 3) 678 ``` 679 """ 680 return self.take(1).next() 681 682 def last(self) -> Option[Item]: 683 """Returns the last item in the iterator. 684 685 Example 686 ------- 687 ```py 688 iterator = Iter([3, 1, 6, 7]) 689 iterator.last().is_some_and(lambda x: x == 7) 690 ``` 691 """ 692 return self.reversed().first() 693 694 def count(self) -> int: 695 """Return the count of elements in memory this iterator has. 696 697 Example 698 ------- 699 ```py 700 it = Iter(range(3)) 701 assert it.count() == 3 702 ``` 703 """ 704 count = 0 705 for _ in self: 706 count += 1 707 708 return count 709 710 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 711 """Searches for an element of an iterator that satisfies a predicate. 712 713 If you want the position of the element, use `Iterator.position` instead. 714 715 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 716 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 717 718 Example 719 ------- 720 ```py 721 it = Iter(range(10)) 722 item = it.find(lambda num: num > 5) 723 print(item) # 6 724 ``` 725 """ 726 for item in self: 727 if predicate(item): 728 return _option.Some(item) 729 730 # no more items 731 return _option.NOTHING 732 733 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 734 """Searches for the position of an element in the iterator that satisfies a predicate. 735 736 If you want the object itself, use `Iterator.find` instead. 737 738 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 739 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 740 741 Example 742 ------- 743 ```py 744 it = Iter(range(10)) 745 position = it.find(lambda num: num > 5) 746 assert position.unwrap() == 6 747 ``` 748 """ 749 for position, value in self.enumerate(): 750 if predicate(value): 751 return _option.Some(position) 752 753 # no more items 754 return _option.NOTHING 755 756 def fold( 757 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 758 ) -> OtherItem: 759 """Folds every element into an accumulator by applying an operation, returning the final result. 760 761 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 762 The closure returns the value that the accumulator should have for the next iteration. 763 764 The initial value is the value the accumulator will have on the first call. 765 766 After applying this closure to every element of the iterator, fold() returns the accumulator. 767 768 This operation is sometimes called ‘reduce’ or ‘inject’. 769 770 Example 771 ------- 772 ```py 773 a = Iter([1, 2, 3, 4]) 774 sum = a.fold(0, lambda acc, elem: acc + elem) 775 assert sum == 10 776 ``` 777 """ 778 accum = init 779 while True: 780 try: 781 x = self.__next__() 782 accum = f(accum, x) 783 except StopIteration: 784 break 785 786 return accum 787 788 def advance_by(self, n: int) -> _result.Result[None, int]: 789 """Advances the iterator by `n` elements. 790 791 Returns `Result[None, int]`, where `Ok(None)` means the iterator 792 advanced successfully, and `Err(int)` if `None` encountered, where `int` 793 represents the remaining number of steps that could not be advanced because the iterator ran out. 794 795 Example 796 ------- 797 ```py 798 it = into_iter([1, 2, 3, 4]) 799 assert it.advance_by(2).is_ok() 800 assert it.next() == Some(3) 801 assert it.advance_by(0).is_ok() 802 assert it.advance_by(100) == Err(99) 803 ``` 804 """ 805 for i in range(n): 806 try: 807 self.__next__() 808 except StopIteration: 809 return _result.Err(n - i) 810 811 return _result.Ok(None) 812 813 def nth(self, n: int) -> Option[Item]: 814 """Returns the `n`th element of the iterator 815 816 Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first 817 value. 818 819 Note all elements before `n` will be skipped / consumed. 820 821 Example 822 ------- 823 ```py 824 a = into_iter([1, 2, 3]) 825 assert a.iter().nth(1) == Some(2) 826 ``` 827 """ 828 for _ in range(n): 829 try: 830 self.__next__() 831 except StopIteration: 832 return _option.NOTHING 833 834 return self.next() 835 836 def sum(self: Sum) -> int: 837 """Sums an iterator of a possible type `T` that can be converted to an integer. 838 839 where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`). 840 841 Example 842 ------- 843 ```py 844 numbers: Iterator[str] = Iter(["1", "2", "3"]) 845 total = numbers.sum() 846 assert total == 6 847 ``` 848 """ 849 return sum(int(_) for _ in self) 850 851 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 852 """Calls `func` on each item in the iterator. 853 854 Example 855 ------- 856 ```py 857 iterator = Iter([1, 2, 3]) 858 iterator.for_each(lambda item: print(item)) 859 # 1 860 # 2 861 # 3 862 ``` 863 864 Parameters 865 ---------- 866 func: `collections.Callable[[Item], typing.Any]` 867 The function to call on each item in the iterator. 868 """ 869 for item in self: 870 func(item) 871 872 async def async_for_each( 873 self, 874 func: collections.Callable[ 875 [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem] 876 ], 877 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 878 """Calls the async function on each item in the iterator *concurrently*. 879 880 Concurrently meaning that the next item will not wait for other items 881 to finish to execute, each item gets called in a separate task. 882 883 After all the tasks finish, a `Result[list[T], JoinError]` will be returned, 884 which will need to be handled by the caller. 885 886 Example 887 ------- 888 ```py 889 async def create_user(username: str) -> None: 890 await aiohttp.request("POST", f'.../{username}') 891 892 async def main(): 893 users = sain.into_iter(["Danny", "Flower"]) 894 match await users.async_for_each(lambda username: create_user(username)): 895 case Ok(result): 896 # all good 897 case Err(why): 898 print(f"couldn't gather all futures, err={why}") 899 ``` 900 901 Parameters 902 ---------- 903 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 904 The async function to call on each item in the iterator. 905 """ 906 return await futures.join(*(func(item) for item in self)) 907 908 def __reversed__(self) -> Iter[Item]: 909 return self.reversed() 910 911 def __repr__(self) -> str: 912 return "<Iterator>" 913 914 def __copy__(self) -> Cloned[Item]: 915 return self.cloned() 916 917 def __deepcopy__( 918 self, memo: collections.MutableMapping[int, typing.Any], / 919 ) -> Copied[Item]: 920 return self.copied() 921 922 def __len__(self) -> int: 923 return self.count() 924 925 def __iter__(self) -> Iterator[Item]: 926 return self 927 928 929class ExactSizeIterator(typing.Generic[Item], Iterator[Item], abc.ABC): 930 """An iterator that knows its exact size. 931 932 The implementations of this interface indicates that the iterator knows exactly 933 how many items it can yield. 934 935 however, this is not a requirement for the iterator to implement this trait, as its 936 only used for iterators that can know their size. 937 938 Example 939 ------- 940 ```py 941 @dataclass 942 class Letters(ExactSizeIterator[str]): 943 letters: list[str] 944 945 def __next__(self) -> str: 946 return self.letters.pop(0) 947 948 def __len__(self) -> int: 949 return len(self.letters) 950 951 letters = Letters(['a', 'b', 'c']) 952 assert letters.count() == 3 953 assert letters.next() == Some('a') 954 assert letters.count() == 2 955 ``` 956 """ 957 958 __slots__ = () 959 960 @typing.final 961 def count(self) -> int: 962 return len(self) 963 964 @typing.final 965 def len(self) -> int: 966 """Returns the remaining number of items in the iterator. 967 968 This doesn't exhaust the iterator. 969 970 Example 971 ------- 972 ```py 973 it = once(0) 974 assert it.len() == 1 975 assert it.len() == 1 976 it.next() 977 assert it.len() == 0 978 ``` 979 """ 980 return len(self) 981 982 @typing.final 983 def is_empty(self) -> bool: 984 """Return `True` if this iterator has no items left to yield. 985 986 Example 987 ------- 988 ```py 989 iterator = once(1) 990 assert not iterator.is_empty() 991 assert once.next() == Some(1) 992 assert iterator.is_empty() 993 ``` 994 """ 995 return len(self) == 0 996 997 @abc.abstractmethod 998 def __len__(self) -> int: ... 999 1000 1001@rustc_diagnostic_item("Iter") 1002@typing.final 1003@diagnostic 1004class Iter(typing.Generic[Item], Iterator[Item]): 1005 """a lazy iterator that has its items ready in-memory. 1006 1007 This is similar to Rust `std::slice::Iter<T>` item which iterables can build 1008 from this via `.iter()` method. 1009 1010 Example 1011 ------- 1012 ```py 1013 iterator = Iter([1, 2, 3]) 1014 1015 # Limit the results to 2. 1016 for item in iterator.take(2): 1017 print(item) 1018 # 1 1019 # 2 1020 1021 # Filter the results. 1022 for item in iterator.filter(lambda item: item > 1): 1023 print(item) 1024 # 2 1025 # 3 1026 # 3 1027 1028 # Indexing is supported. 1029 print(iterator[0]) 1030 # 1 1031 ``` 1032 1033 Parameters 1034 ---------- 1035 items: `Iterable[Item]` 1036 The items to iterate over. This can be anything that implements `__iter__` and `__next__`. 1037 """ 1038 1039 __slots__ = ("_it",) 1040 1041 def __init__(self, iterable: collections.Iterable[Item]) -> None: 1042 self._it = iter(iterable) 1043 1044 def clone(self) -> Iter[Item]: 1045 """Return a copy of this iterator. 1046 1047 ```py 1048 it = Iterator([1, 2, 3]) 1049 1050 for i in it.clone(): 1051 ... 1052 1053 # The actual iterator hasn't been exhausted. 1054 assert it.count() == 3 1055 ``` 1056 """ 1057 return Iter(copy.copy(self._it)) 1058 1059 def __next__(self) -> Item: 1060 return next(self._it) 1061 1062 def __getitem__(self, index: int) -> Item: 1063 return self.skip(index).first().unwrap_or_else(oob) 1064 1065 def __contains__(self, item: Item) -> bool: 1066 return item in self._it 1067 1068 1069@typing.final 1070class TrustedIter(typing.Generic[Item], ExactSizeIterator[Item]): 1071 """Similar to `Iter`, but it reports an accurate length using `ExactSizeIterator`. 1072 1073 iterable objects such as `Vec`, `Bytes`, `list` and other `Sized` may be created 1074 using this iterator. 1075 1076 Example 1077 ------- 1078 ```py 1079 # we know the size of the iterator. 1080 sized_buf: TrustedIter[int] = into_iter((1, 2, 3, 4)) 1081 # this is `Iter[int]` since we don't know when the generator will stop yielding. 1082 unsized_buf: Iter[int] = into_iter((_ for _ in ([1, 2, 3, 4] if cond else [1, 2]))) 1083 ``` 1084 1085 Parameters 1086 ---------- 1087 items: `collections.Collection[Item]` 1088 A sized collection of items to iterate over. 1089 """ 1090 1091 __slots__ = ("_it", "_len", "__alive") 1092 1093 def __init__(self, iterable: collections.Sequence[Item]) -> None: 1094 self.__alive = iterable 1095 self._len = len(iterable) 1096 self._it = iter(iterable) 1097 1098 @property 1099 def __slice_checked_get(self) -> collections.Sequence[Item] | None: 1100 try: 1101 return self.__alive 1102 except AttributeError: 1103 return None 1104 1105 def next(self) -> Option[Item]: 1106 if self._len == 0: 1107 # ! SAFETY: len == 0 1108 return _option.NOTHING 1109 1110 return _option.Some(self.__next__()) 1111 1112 @unsafe 1113 def next_unchecked(self) -> Item: 1114 """Returns the next item in the iterator without checking if it exists. 1115 1116 This is equivalent to calling `next()` on the iterator directly. 1117 1118 Example 1119 ------- 1120 ```py 1121 iterator = Iter([1]) 1122 assert iterator.next_unchecked() == 1 1123 iterator.next_unchecked() # raises StopIteration 1124 ``` 1125 """ 1126 return self.__next__() 1127 1128 @unsafe 1129 def set_len(self, new_len: int) -> None: 1130 """Sets the length of the iterator to `new_len`. 1131 1132 This is unsafe and should only be used if you know what you're doing. 1133 1134 Example 1135 ------- 1136 ```py 1137 iterator = Iter([1, 2, 3]) 1138 iterator.set_len(2) 1139 assert iterator.len() == 2 1140 ``` 1141 """ 1142 self._len = new_len 1143 1144 def as_slice(self) -> Slice[Item]: 1145 """Returns an immutable slice of all elements that have not been yielded 1146 1147 Example 1148 ------- 1149 ```py 1150 iterator = into_iter([1, 2, 3]) 1151 iterator.as_slice() == [1, 2, 3] 1152 iterator.next() 1153 assert iterator.as_slice() == [2, 3] 1154 ``` 1155 """ 1156 from .collections.slice import Slice 1157 1158 return Slice(self.__slice_checked_get or ()) 1159 1160 def __repr__(self) -> str: 1161 # __alive is dropped from `self`. 1162 if (s := self.__slice_checked_get) is None: 1163 return "TrustedIter(<empty>)" 1164 1165 return f"TrustedIter({s[-self._len :]})" 1166 1167 def __next__(self) -> Item: 1168 try: 1169 i = next(self._it) 1170 except StopIteration: 1171 # don't reference this anymore. 1172 del self.__alive 1173 raise 1174 1175 self._len -= 1 1176 return i 1177 1178 def __getitem__(self, index: int) -> Item: 1179 if self._len == 0: 1180 raise IndexError("index out of bounds") 1181 1182 return self.skip(index).first().unwrap_or_else(oob) 1183 1184 def __contains__(self, item: Item) -> bool: 1185 return item in self._it 1186 1187 def __len__(self) -> int: 1188 return self._len 1189 1190 1191@diagnostic 1192class Cloned(typing.Generic[Item], Iterator[Item]): 1193 """An iterator that copies the elements from an underlying iterator. 1194 1195 This iterator is created by the `Iterator.cloned` method. 1196 """ 1197 1198 __slots__ = ("_it",) 1199 1200 def __init__(self, it: Iterator[Item]) -> None: 1201 self._it = it 1202 1203 def __next__(self) -> Item: 1204 n = self._it.__next__() 1205 1206 # Avoid useless function call for a list. 1207 if isinstance(n, list): 1208 # SAFETY: We know this is a list. 1209 return n[:] # pyright: ignore 1210 1211 return copy.copy(n) 1212 1213 1214@diagnostic 1215class Copied(typing.Generic[Item], Iterator[Item]): 1216 """An iterator that deeply-copies the elements from an underlying iterator. 1217 1218 This iterator is created by the `Iterator.copied` method. 1219 """ 1220 1221 __slots__ = ("_it",) 1222 1223 def __init__(self, it: Iterator[Item]) -> None: 1224 self._it = it 1225 1226 def __next__(self) -> Item: 1227 return copy.deepcopy(self._it.__next__()) 1228 1229 1230@diagnostic 1231class Map(typing.Generic[Item, OtherItem], Iterator[OtherItem]): 1232 """An iterator that maps the elements to a callable. 1233 1234 This iterator is created by the `Iterator.map` method. 1235 """ 1236 1237 __slots__ = ("_it", "_call") 1238 1239 def __init__( 1240 self, it: Iterator[Item], call: collections.Callable[[Item], OtherItem] 1241 ) -> None: 1242 self._it = it 1243 self._call = call 1244 1245 def __next__(self) -> OtherItem: 1246 return self._call(self._it.__next__()) 1247 1248 1249@diagnostic 1250class Filter(typing.Generic[Item], Iterator[Item]): 1251 """An iterator that filters the elements to a `predicate`. 1252 1253 This iterator is created by the `Iterator.filter` method. 1254 """ 1255 1256 __slots__ = ("_it", "_call") 1257 1258 def __init__( 1259 self, it: Iterator[Item], call: collections.Callable[[Item], bool] 1260 ) -> None: 1261 self._it = it 1262 self._call = call 1263 1264 def __next__(self) -> Item: 1265 for item in self._it: 1266 if self._call(item): 1267 return item 1268 1269 unreachable() 1270 1271 1272@diagnostic 1273class Take(typing.Generic[Item], Iterator[Item]): 1274 """An iterator that yields the first `number` of elements and drops the rest. 1275 1276 This iterator is created by the `Iterator.take` method. 1277 """ 1278 1279 __slots__ = ("_it", "_taken", "_count") 1280 1281 def __init__(self, it: Iterator[Item], count: int) -> None: 1282 if count <= 0: 1283 raise ValueError("`count` must be non-zero") 1284 1285 self._it = it 1286 self._taken = count 1287 self._count = 0 1288 1289 def __next__(self) -> Item: 1290 if self._count >= self._taken: 1291 unreachable() 1292 1293 item = self._it.__next__() 1294 self._count += 1 1295 return item 1296 1297 1298@diagnostic 1299class Skip(typing.Generic[Item], Iterator[Item]): 1300 """An iterator that skips the first `number` of elements and yields the rest. 1301 1302 This iterator is created by the `Iterator.skip` method. 1303 """ 1304 1305 __slots__ = ("_it", "_count", "_skipped") 1306 1307 def __init__(self, it: Iterator[Item], count: int) -> None: 1308 if count <= 0: 1309 raise ValueError("`count` must be non-zero") 1310 1311 self._it = it 1312 self._count = count 1313 self._skipped = 0 1314 1315 def __next__(self) -> Item: 1316 while self._skipped < self._count: 1317 self._skipped += 1 1318 self._it.__next__() 1319 1320 return self._it.__next__() 1321 1322 1323@diagnostic 1324class Enumerate(typing.Generic[Item], Iterator[tuple[int, Item]]): 1325 """An iterator that yields the current count and the element during iteration. 1326 1327 This iterator is created by the `Iterator.enumerate` method. 1328 """ 1329 1330 __slots__ = ("_it", "_count") 1331 1332 def __init__(self, it: Iterator[Item], start: int) -> None: 1333 self._it = it 1334 self._count = start 1335 1336 def __next__(self) -> tuple[int, Item]: 1337 a = self._it.__next__() 1338 i = self._count 1339 self._count += 1 1340 return i, a 1341 1342 1343@diagnostic 1344class TakeWhile(typing.Generic[Item], Iterator[Item]): 1345 """An iterator that yields elements while `predicate` returns `True`. 1346 1347 This iterator is created by the `Iterator.take_while` method. 1348 """ 1349 1350 __slots__ = ("_it", "_predicate") 1351 1352 def __init__( 1353 self, it: Iterator[Item], predicate: collections.Callable[[Item], bool] 1354 ) -> None: 1355 self._it = it 1356 self._predicate = predicate 1357 1358 def __next__(self) -> Item: 1359 item = self._it.__next__() 1360 1361 if self._predicate(item): 1362 return item 1363 1364 unreachable() 1365 1366 1367@diagnostic 1368class DropWhile(typing.Generic[Item], Iterator[Item]): 1369 """An iterator that yields elements while `predicate` returns `False`. 1370 1371 This iterator is created by the `Iterator.drop_while` method. 1372 """ 1373 1374 __slots__ = ("_it", "_predicate", "_dropped") 1375 1376 def __init__( 1377 self, it: Iterator[Item], predicate: collections.Callable[[Item], bool] 1378 ) -> None: 1379 self._it = it 1380 self._predicate = predicate 1381 self._dropped = False 1382 1383 def __next__(self) -> Item: 1384 if not self._dropped: 1385 while not self._predicate(item := self._it.__next__()): 1386 pass 1387 1388 self._dropped = True 1389 return item 1390 1391 unreachable() 1392 1393 1394@diagnostic 1395class Chunks(typing.Generic[Item], Iterator[collections.Sequence[Item]]): 1396 """An iterator that yields elements in chunks. 1397 1398 This iterator is created by the `Iterator.chunks` method. 1399 """ 1400 1401 __slots__ = ("chunk_size", "_it") 1402 1403 def __init__(self, it: Iterator[Item], chunk_size: int) -> None: 1404 self.chunk_size = chunk_size 1405 self._it = it 1406 1407 def __next__(self) -> collections.Sequence[Item]: 1408 chunk: list[Item] = [] 1409 1410 for item in self._it: 1411 chunk.append(item) 1412 1413 if len(chunk) == self.chunk_size: 1414 break 1415 1416 if chunk: 1417 return chunk 1418 1419 unreachable() 1420 1421 1422@typing.final 1423@diagnostic 1424class Empty(typing.Generic[Item], ExactSizeIterator[Item]): 1425 """An iterator that yields nothing. 1426 1427 This is the default iterator that is created by `Iterator.default()` or `empty()` 1428 """ 1429 1430 __slots__ = () 1431 1432 def __init__(self) -> None: 1433 pass 1434 1435 def next(self) -> Option[Item]: 1436 # SAFETY: an empty iterator always returns None. 1437 # also we avoid calling `nothing_unchecked()` here for fast returns. 1438 return _option.NOTHING 1439 1440 def __len__(self) -> typing.Literal[0]: 1441 return 0 1442 1443 def any( 1444 self, predicate: collections.Callable[[Item], bool] 1445 ) -> typing.Literal[False]: 1446 return False 1447 1448 def all( 1449 self, predicate: collections.Callable[[Item], bool] 1450 ) -> typing.Literal[False]: 1451 return False 1452 1453 def __next__(self) -> Item: 1454 unreachable() 1455 1456 1457@typing.final 1458@diagnostic 1459class Repeat(typing.Generic[Item], ExactSizeIterator[Item]): 1460 """An iterator that repeats a given value an exact number of times. 1461 1462 This iterator is created by calling `repeat()`. 1463 """ 1464 1465 __slots__ = ("_count", "_element") 1466 1467 def __init__(self, element: Item, count: int) -> None: 1468 self._count = count 1469 self._element = element 1470 1471 def __next__(self) -> Item: 1472 if self._count > 0: 1473 self._count -= 1 1474 if self._count == 0: 1475 # Return the origin element last 1476 return self._element 1477 1478 return copy.copy(self._element) 1479 1480 unreachable() 1481 1482 def __len__(self) -> int: 1483 return self._count 1484 1485 1486@typing.final 1487@diagnostic 1488class Once(typing.Generic[Item], ExactSizeIterator[Item]): 1489 """An iterator that yields exactly one item. 1490 1491 This iterator is created by calling `once()`. 1492 """ 1493 1494 __slots__ = ("_item",) 1495 1496 def __init__(self, item: Item) -> None: 1497 self._item: Item | None = item 1498 1499 def __next__(self) -> Item: 1500 if self._item is None: 1501 unreachable() 1502 1503 i = self._item 1504 self._item = None 1505 return i 1506 1507 def __len__(self) -> int: 1508 return 1 if self._item is not None else 0 1509 1510 1511# a hack to trick the type-checker into thinking that this iterator yield `Item`. 1512@rustc_diagnostic_item("empty") 1513def empty() -> Empty[Item]: # pyright: ignore 1514 """Create an iterator that yields nothing. 1515 1516 Example 1517 ------- 1518 ```py 1519 nope: Iterator[int] = sain.iter.empty() 1520 assert nope.next().is_none() 1521 ``` 1522 """ 1523 return Empty() 1524 1525 1526@rustc_diagnostic_item("repeat") 1527def repeat(element: Item, count: int) -> Repeat[Item]: 1528 """Returns an iterator that yields the exact same `element` number of `count` times. 1529 1530 The yielded elements is a copy of `element`, but the last element is guaranteed to be the same as the 1531 original `element`. 1532 1533 Example 1534 ------- 1535 ```py 1536 nums = [1, 2, 3] 1537 it = sain.iter.repeat(nums, 5) 1538 for i in range(4): 1539 cloned = it.next().unwrap() 1540 assert cloned == [1, 2, 3] 1541 1542 # But the last item is the origin one... 1543 last = it.next().unwrap() 1544 last.append(4) 1545 assert nums == [1, 2, 3, 4] 1546 ``` 1547 """ 1548 return Repeat(element, count) 1549 1550 1551@rustc_diagnostic_item("once") 1552def once(item: Item) -> Once[Item]: 1553 """Returns an iterator that yields exactly a single item. 1554 1555 Example 1556 ------- 1557 ```py 1558 iterator = sain.iter.once(1) 1559 assert iterator.next() == Some(1) 1560 assert iterator.next() == Some(None) 1561 ``` 1562 """ 1563 return Once(item) 1564 1565 1566@typing.overload 1567def into_iter( 1568 iterable: collections.Sequence[Item], 1569) -> TrustedIter[Item]: ... 1570 1571 1572@typing.overload 1573def into_iter( 1574 iterable: collections.Iterable[Item], 1575 /, 1576) -> Iter[Item]: ... 1577 1578 1579@rustc_diagnostic_item("into_iter") 1580def into_iter( 1581 iterable: collections.Sequence[Item] | collections.Iterable[Item], 1582) -> Iter[Item] | TrustedIter[Item] | TrustedIter[int]: 1583 """Convert any iterable into `Iterator[Item]`. 1584 1585 if the size of the iterable is known, it will return `TrustedIter`, 1586 otherwise it will return `Iter`. 1587 1588 Example 1589 ------- 1590 ```py 1591 sequence = [1,2,3] 1592 for item in sain.into_iter(sequence).reversed(): 1593 print(item) 1594 # 3 1595 # 2 1596 # 1 1597 ``` 1598 """ 1599 if isinstance(iterable, collections.Sequence): 1600 return TrustedIter(iterable) 1601 return Iter(iterable)
1002@rustc_diagnostic_item("Iter") 1003@typing.final 1004@diagnostic 1005class Iter(typing.Generic[Item], Iterator[Item]): 1006 """a lazy iterator that has its items ready in-memory. 1007 1008 This is similar to Rust `std::slice::Iter<T>` item which iterables can build 1009 from this via `.iter()` method. 1010 1011 Example 1012 ------- 1013 ```py 1014 iterator = Iter([1, 2, 3]) 1015 1016 # Limit the results to 2. 1017 for item in iterator.take(2): 1018 print(item) 1019 # 1 1020 # 2 1021 1022 # Filter the results. 1023 for item in iterator.filter(lambda item: item > 1): 1024 print(item) 1025 # 2 1026 # 3 1027 # 3 1028 1029 # Indexing is supported. 1030 print(iterator[0]) 1031 # 1 1032 ``` 1033 1034 Parameters 1035 ---------- 1036 items: `Iterable[Item]` 1037 The items to iterate over. This can be anything that implements `__iter__` and `__next__`. 1038 """ 1039 1040 __slots__ = ("_it",) 1041 1042 def __init__(self, iterable: collections.Iterable[Item]) -> None: 1043 self._it = iter(iterable) 1044 1045 def clone(self) -> Iter[Item]: 1046 """Return a copy of this iterator. 1047 1048 ```py 1049 it = Iterator([1, 2, 3]) 1050 1051 for i in it.clone(): 1052 ... 1053 1054 # The actual iterator hasn't been exhausted. 1055 assert it.count() == 3 1056 ``` 1057 """ 1058 return Iter(copy.copy(self._it)) 1059 1060 def __next__(self) -> Item: 1061 return next(self._it) 1062 1063 def __getitem__(self, index: int) -> Item: 1064 return self.skip(index).first().unwrap_or_else(oob) 1065 1066 def __contains__(self, item: Item) -> bool: 1067 return item in self._it
a lazy iterator that has its items ready in-memory.
This is similar to Rust std::slice::Iter<T> item which iterables can build
from this via sain.iter method.
Example
iterator = Iter([1, 2, 3])
# Limit the results to 2.
for item in iterator.take(2):
print(item)
# 1
# 2
# Filter the results.
for item in iterator.filter(lambda item: item > 1):
print(item)
# 2
# 3
# 3
# Indexing is supported.
print(iterator[0])
# 1
Parameters
- items (
Iterable[Item]): The items to iterate over. This can be anything that implements__iter__and__next__. - # Implementations
- **This class implements Iter:
1045 def clone(self) -> Iter[Item]: 1046 """Return a copy of this iterator. 1047 1048 ```py 1049 it = Iterator([1, 2, 3]) 1050 1051 for i in it.clone(): 1052 ... 1053 1054 # The actual iterator hasn't been exhausted. 1055 assert it.count() == 3 1056 ``` 1057 """ 1058 return Iter(copy.copy(self._it))
Return a copy of this iterator.
it = Iterator([1, 2, 3])
for i in it.clone():
...
# The actual iterator hasn't been exhausted.
assert it.count() == 3
125@rustc_diagnostic_item("Iterator") 126class Iterator( 127 typing.Generic[Item], 128 abc.ABC, 129 _default.Default["Empty[Item]"], 130): 131 """An abstract interface for dealing with iterators. 132 133 This is exactly the same trait as `core::iter::Iterator` trait from Rust. 134 135 This is the main interface that any type can implement by basically inheriting from it. 136 The method `__next__` is the only method that needs to be implemented, You get all the other methods for free. 137 138 If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementers 139 and type hints. 140 141 Example 142 ------- 143 ```py 144 @dataclass 145 class Counter(Iterator[int]): 146 start: int = 0 147 stop: int | None = None 148 149 # implement the required method. 150 def __next__(self) -> int: 151 result = self.start 152 self.start += 1 153 154 if self.stop is not None and result >= self.stop: 155 raise StopIteration 156 157 return result 158 159 counter = Counter(start=0, stop=10) 160 for i in counter.map(lambda x: x * 2): # multiply each number 161 ... 162 ``` 163 """ 164 165 __slots__ = () 166 167 @abc.abstractmethod 168 def __next__(self) -> Item: 169 raise NotImplementedError 170 171 ################### 172 # const functions # 173 ################### 174 175 @staticmethod 176 @typing.final 177 def default() -> Empty[Item]: 178 """Return the default iterator for this type. It returns an empty iterator. 179 180 Example 181 ------- 182 ```py 183 it: Iterator[int] = Iter.default() 184 assert t.next().is_none() 185 ``` 186 """ 187 return Empty() 188 189 @typing.overload 190 def collect(self) -> collections.MutableSequence[Item]: ... 191 192 @typing.overload 193 def collect( 194 self, *, cast: collections.Callable[[Item], OtherItem] 195 ) -> collections.MutableSequence[OtherItem]: ... 196 197 @typing.final 198 def collect( 199 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 200 ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]: 201 """Collects all items in the iterator into a sequence. 202 203 Example 204 ------- 205 ```py 206 iterator = Iter(range(3)) 207 iterator.collect() 208 # (0, 1, 2, 3) 209 iterator.collect(cast=str) # Map each element and collect it. 210 # ('0', '1', '2', '3') 211 ``` 212 213 Parameters 214 ---------- 215 cast: `T | None` 216 An optional type to cast the items into. 217 If not provided the items will be returned as it's original type. 218 """ 219 if cast is not None: 220 return list(cast(i) for i in self) 221 222 return list(_ for _ in self) 223 224 @typing.final 225 def collect_into(self, collection: Collector[Item]) -> None: 226 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 227 228 Example 229 ------- 230 ```py 231 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 232 uniques = set() 233 iterator.collect_into(uniques) 234 # assert uniques == {1, 2, 3, 4, 6} 235 ``` 236 237 Parameters 238 ---------- 239 collection: `MutableSequence[T]` | `set[T]` 240 The collection to extend the items in this iterator with. 241 """ 242 if isinstance(collection, collections.MutableSequence): 243 collection.extend(_ for _ in self) 244 elif isinstance(collection, collections.MutableSet): 245 collection.update(_ for _ in self) 246 else: 247 for idx, item in enumerate(self): 248 collection[idx] = item 249 250 @typing.final 251 def to_vec(self) -> Vec[Item]: 252 """Convert this iterator into `Vec[T]`. 253 254 Example 255 ------- 256 ```py 257 it = sain.iter.once(0) 258 vc = it.to_vec() 259 260 assert to_vec == [0] 261 ``` 262 """ 263 from .collections.vec import Vec 264 265 return Vec(_ for _ in self) 266 267 @typing.final 268 def sink(self) -> None: 269 """Consume all elements from this iterator, flushing it into the sink. 270 271 Example 272 ------- 273 ```py 274 it = Iter((1, 2, 3)) 275 it.sink() 276 assert it.next().is_none() 277 ``` 278 """ 279 for _ in self: 280 pass 281 282 @typing.final 283 def raw_parts(self) -> collections.Generator[Item, None, None]: 284 """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items. 285 286 This mainly used for objects that needs to satisfy its exact type. 287 288 ```py 289 it = Iter("cba") 290 sort = sorted(it.raw_parts()) 291 292 assert it.count() == 0 293 assert sort == ["a", "b", "c"] 294 ``` 295 """ 296 for item in self: 297 yield item 298 299 ################## 300 # default impl's # 301 ################## 302 303 def next(self) -> Option[Item]: 304 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 305 306 Example 307 ------- 308 ```py 309 iterator = Iter(["1", "2"]) 310 assert iterator.next() == Some("1") 311 assert iterator.next() == Some("2") 312 assert iterator.next().is_none() 313 ``` 314 """ 315 try: 316 return _option.Some(self.__next__()) 317 except StopIteration: 318 # ! SAFETY: No more items in the iterator. 319 return _option.NOTHING 320 321 def cloned(self) -> Cloned[Item]: 322 """Creates an iterator which shallow copies its elements by reference. 323 324 If you need a copy of the actual iterator and not the elements. 325 use `Iter.clone()` 326 327 .. note:: 328 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 329 on each item that is being yielded. 330 331 Example 332 ------- 333 ```py 334 @dataclass 335 class User: 336 users_ids: list[int] = [] 337 338 # An iterator which elements points to the same user. 339 user = User() 340 it = Iter((user, user)) 341 342 for u in it.cloned(): 343 u.user_ids.append(1) 344 345 # We iterated over the same user pointer twice and appended "1" 346 # since `copy` returns a shallow copy of nested structures. 347 assert len(user.user_ids) == 2 348 ``` 349 """ 350 return Cloned(self) 351 352 def copied(self) -> Copied[Item]: 353 """Creates an iterator which copies all of its elements by value. 354 355 If you only need a copy of the item reference, Use `.cloned()` instead. 356 357 .. note:: 358 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 359 on each item that is being yielded. 360 361 Example 362 ------- 363 ```py 364 @dataclass 365 class User: 366 users_ids: list[int] = [] 367 368 # An iterator which elements points to the same user. 369 user = User() 370 it = Iter((user, user)) 371 372 for u in it.copied(): 373 # A new list is created for each item. 374 u.user_ids.append(1) 375 376 # The actual list is untouched since we consumed a deep copy of it. 377 assert len(user.user_ids) == 0 378 ``` 379 """ 380 return Copied(self) 381 382 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 383 """Maps each item in the iterator to another type. 384 385 Example 386 ------- 387 ```py 388 iterator = Iter(["1", "2", "3"]).map(int) 389 390 for item in iterator: 391 assert isinstance(item, int) 392 ``` 393 394 Parameters 395 ---------- 396 predicate: `Callable[[Item], OtherItem]` 397 The function to map each item in the iterator to the other type. 398 """ 399 return Map(self, fn) 400 401 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 402 """Filters the iterator to only yield items that match the predicate. 403 404 Example 405 ------- 406 ```py 407 places = Iter(['London', 'Paris', 'Los Angeles']) 408 for place in places.filter(lambda place: place.startswith('L')): 409 print(place) 410 411 # London 412 # Los Angeles 413 ``` 414 """ 415 return Filter(self, predicate) 416 417 def take(self, count: int) -> Take[Item]: 418 """Take the first number of items until the number of items 419 are yielded or the end of the iterator is exhausted. 420 421 Example 422 ------- 423 ```py 424 iterator = Iter(['c', 'x', 'y']) 425 426 for x in iterator.take(2): 427 assert x in ('c', 'x') 428 429 # <Iter(['c', 'x'])> 430 ``` 431 """ 432 return Take(self, count) 433 434 def skip(self, count: int) -> Skip[Item]: 435 """Skips the first number of items in the iterator. 436 437 Example 438 ------- 439 ```py 440 iterator = Iter((1, 2, 3, 4)) 441 for i in iterator.skip(2): 442 print(i) 443 444 # 3 445 # 4 446 ``` 447 """ 448 return Skip(self, count) 449 450 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 451 """Create a new iterator that yields a tuple of the index and item. 452 453 Example 454 ------- 455 ```py 456 iterator = Iter([1, 2, 3]) 457 for index, item in iterator.enumerate(): 458 print(index, item) 459 460 # 0 1 461 # 1 2 462 # 2 3 463 ``` 464 """ 465 return Enumerate(self, start) 466 467 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 468 """yields items from the iterator while predicate returns `True`. 469 470 The rest of the items are discarded as soon as the predicate returns `False` 471 472 Example 473 ------- 474 ```py 475 iterator = Iter(['a', 'ab', 'xd', 'ba']) 476 for x in iterator.take_while(lambda x: 'a' in x): 477 print(x) 478 479 # a 480 # ab 481 ``` 482 483 Parameters 484 ---------- 485 predicate: `collections.Callable[[Item], bool]` 486 The function to predicate each item in the iterator. 487 """ 488 return TakeWhile(self, f) 489 490 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 491 """Yields items from the iterator while predicate returns `False`. 492 493 Example 494 ------- 495 ```py 496 iterator = Iter(['a', 'ab', 'xd', 'ba']) 497 for x in iterator.drop_while(lambda x: 'a' in x): 498 print(x) 499 500 # xd 501 # ba 502 ``` 503 504 Parameters 505 ---------- 506 predicate: `collections.Callable[[Item], bool]` 507 The function to predicate each item in the iterator. 508 """ 509 return DropWhile(self, f) 510 511 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 512 """Returns an iterator over `chunk_size` elements of the iterator at a time, 513 starting at the beginning of the iterator. 514 515 Example 516 ------- 517 ```py 518 iter = Iter(['a', 'b', 'c', 'd', 'e']) 519 chunks = iter.chunks() 520 assert chunks.next().unwrap() == ['a', 'b'] 521 assert chunks.next().unwrap() == ['c', 'd'] 522 assert chunks.next().unwrap() == ['e'] 523 assert chunks.next().is_none() 524 ``` 525 """ 526 return Chunks(self, chunk_size) 527 528 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 529 """Return `True` if all items in the iterator match the predicate. 530 531 Example 532 ------- 533 ```py 534 iterator = Iter([1, 2, 3]) 535 while iterator.all(lambda item: isinstance(item, int)): 536 print("Still all integers") 537 continue 538 # Still all integers 539 ``` 540 541 Parameters 542 ---------- 543 predicate: `collections.Callable[[Item], bool]` 544 The function to test each item in the iterator. 545 """ 546 return all(predicate(item) for item in self) 547 548 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 549 """`True` if any items in the iterator match the predicate. 550 551 Example 552 ------- 553 ```py 554 iterator = Iter([1, 2, 3]) 555 if iterator.any(lambda item: isinstance(item, int)): 556 print("At least one item is an int.") 557 # At least one item is an int. 558 ``` 559 560 Parameters 561 ---------- 562 predicate: `collections.Callable[[Item], bool]` 563 The function to test each item in the iterator. 564 """ 565 return any(predicate(item) for item in self) 566 567 def zip( 568 self, other: collections.Iterable[OtherItem] 569 ) -> Iter[tuple[Item, OtherItem]]: 570 """Zips the iterator with another iterable. 571 572 Example 573 ------- 574 ```py 575 iterator = Iter([1, 2, 3]) 576 for item, other_item in iterator.zip([4, 5, 6]): 577 assert item == other_item 578 <Iter([(1, 4), (2, 5), (3, 6)])> 579 ``` 580 581 Parameters 582 ---------- 583 other: `Iter[OtherItem]` 584 The iterable to zip with. 585 586 Returns 587 ------- 588 `Iter[tuple[Item, OtherItem]]` 589 The zipped iterator. 590 591 """ 592 return Iter(zip(self.raw_parts(), other)) 593 594 def sort( 595 self, 596 *, 597 key: collections.Callable[[Item], _typeshed.SupportsRichComparison], 598 reverse: bool = False, 599 ) -> Iter[Item]: 600 """Sorts the iterator. 601 602 Example 603 ------- 604 ```py 605 iterator = Iter([3, 1, 6, 7]) 606 for item in iterator.sort(key=lambda item: item < 3): 607 print(item) 608 # 1 609 # 3 610 # 6 611 # 7 612 ``` 613 614 Parameters 615 ---------- 616 key: `collections.Callable[[Item], Any]` 617 The function to sort by. 618 reverse: `bool` 619 Whether to reverse the sort. 620 """ 621 return Iter(sorted(self.raw_parts(), key=key, reverse=reverse)) 622 623 def reversed(self) -> Iter[Item]: 624 """Returns a new iterator that yields the items in the iterator in reverse order. 625 626 This consumes this iterator into a sequence and return a new iterator containing all of the elements 627 in reversed order. 628 629 Example 630 ------- 631 ```py 632 iterator = Iter([3, 1, 6, 7]) 633 for item in iterator.reversed(): 634 print(item) 635 # 7 636 # 6 637 # 1 638 # 3 639 ``` 640 """ 641 # NOTE: In order to reverse the iterator we need to 642 # first collect it into some collection. 643 return Iter(reversed(list(_ for _ in self))) 644 645 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 646 """Returns a new iterator that yields all items from both iterators. 647 648 Example 649 ------- 650 ```py 651 iterator = Iter([1, 2, 3]) 652 other = [4, 5, 6] 653 654 for item in iterator.union(other): 655 print(item) 656 # 1 657 # 2 658 # 3 659 # 4 660 # 5 661 # 6 662 ``` 663 664 Parameters 665 ---------- 666 other: `Iter[Item]` 667 The iterable to union with. 668 """ 669 return Iter(itertools.chain(self.raw_parts(), other)) 670 671 def first(self) -> Option[Item]: 672 """Returns the first item in the iterator. 673 674 Example 675 ------- 676 ```py 677 iterator = Iter([3, 1, 6, 7]) 678 iterator.first().is_some_and(lambda x: x == 3) 679 ``` 680 """ 681 return self.take(1).next() 682 683 def last(self) -> Option[Item]: 684 """Returns the last item in the iterator. 685 686 Example 687 ------- 688 ```py 689 iterator = Iter([3, 1, 6, 7]) 690 iterator.last().is_some_and(lambda x: x == 7) 691 ``` 692 """ 693 return self.reversed().first() 694 695 def count(self) -> int: 696 """Return the count of elements in memory this iterator has. 697 698 Example 699 ------- 700 ```py 701 it = Iter(range(3)) 702 assert it.count() == 3 703 ``` 704 """ 705 count = 0 706 for _ in self: 707 count += 1 708 709 return count 710 711 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 712 """Searches for an element of an iterator that satisfies a predicate. 713 714 If you want the position of the element, use `Iterator.position` instead. 715 716 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 717 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 718 719 Example 720 ------- 721 ```py 722 it = Iter(range(10)) 723 item = it.find(lambda num: num > 5) 724 print(item) # 6 725 ``` 726 """ 727 for item in self: 728 if predicate(item): 729 return _option.Some(item) 730 731 # no more items 732 return _option.NOTHING 733 734 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 735 """Searches for the position of an element in the iterator that satisfies a predicate. 736 737 If you want the object itself, use `Iterator.find` instead. 738 739 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 740 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 741 742 Example 743 ------- 744 ```py 745 it = Iter(range(10)) 746 position = it.find(lambda num: num > 5) 747 assert position.unwrap() == 6 748 ``` 749 """ 750 for position, value in self.enumerate(): 751 if predicate(value): 752 return _option.Some(position) 753 754 # no more items 755 return _option.NOTHING 756 757 def fold( 758 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 759 ) -> OtherItem: 760 """Folds every element into an accumulator by applying an operation, returning the final result. 761 762 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 763 The closure returns the value that the accumulator should have for the next iteration. 764 765 The initial value is the value the accumulator will have on the first call. 766 767 After applying this closure to every element of the iterator, fold() returns the accumulator. 768 769 This operation is sometimes called ‘reduce’ or ‘inject’. 770 771 Example 772 ------- 773 ```py 774 a = Iter([1, 2, 3, 4]) 775 sum = a.fold(0, lambda acc, elem: acc + elem) 776 assert sum == 10 777 ``` 778 """ 779 accum = init 780 while True: 781 try: 782 x = self.__next__() 783 accum = f(accum, x) 784 except StopIteration: 785 break 786 787 return accum 788 789 def advance_by(self, n: int) -> _result.Result[None, int]: 790 """Advances the iterator by `n` elements. 791 792 Returns `Result[None, int]`, where `Ok(None)` means the iterator 793 advanced successfully, and `Err(int)` if `None` encountered, where `int` 794 represents the remaining number of steps that could not be advanced because the iterator ran out. 795 796 Example 797 ------- 798 ```py 799 it = into_iter([1, 2, 3, 4]) 800 assert it.advance_by(2).is_ok() 801 assert it.next() == Some(3) 802 assert it.advance_by(0).is_ok() 803 assert it.advance_by(100) == Err(99) 804 ``` 805 """ 806 for i in range(n): 807 try: 808 self.__next__() 809 except StopIteration: 810 return _result.Err(n - i) 811 812 return _result.Ok(None) 813 814 def nth(self, n: int) -> Option[Item]: 815 """Returns the `n`th element of the iterator 816 817 Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first 818 value. 819 820 Note all elements before `n` will be skipped / consumed. 821 822 Example 823 ------- 824 ```py 825 a = into_iter([1, 2, 3]) 826 assert a.iter().nth(1) == Some(2) 827 ``` 828 """ 829 for _ in range(n): 830 try: 831 self.__next__() 832 except StopIteration: 833 return _option.NOTHING 834 835 return self.next() 836 837 def sum(self: Sum) -> int: 838 """Sums an iterator of a possible type `T` that can be converted to an integer. 839 840 where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`). 841 842 Example 843 ------- 844 ```py 845 numbers: Iterator[str] = Iter(["1", "2", "3"]) 846 total = numbers.sum() 847 assert total == 6 848 ``` 849 """ 850 return sum(int(_) for _ in self) 851 852 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 853 """Calls `func` on each item in the iterator. 854 855 Example 856 ------- 857 ```py 858 iterator = Iter([1, 2, 3]) 859 iterator.for_each(lambda item: print(item)) 860 # 1 861 # 2 862 # 3 863 ``` 864 865 Parameters 866 ---------- 867 func: `collections.Callable[[Item], typing.Any]` 868 The function to call on each item in the iterator. 869 """ 870 for item in self: 871 func(item) 872 873 async def async_for_each( 874 self, 875 func: collections.Callable[ 876 [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem] 877 ], 878 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 879 """Calls the async function on each item in the iterator *concurrently*. 880 881 Concurrently meaning that the next item will not wait for other items 882 to finish to execute, each item gets called in a separate task. 883 884 After all the tasks finish, a `Result[list[T], JoinError]` will be returned, 885 which will need to be handled by the caller. 886 887 Example 888 ------- 889 ```py 890 async def create_user(username: str) -> None: 891 await aiohttp.request("POST", f'.../{username}') 892 893 async def main(): 894 users = sain.into_iter(["Danny", "Flower"]) 895 match await users.async_for_each(lambda username: create_user(username)): 896 case Ok(result): 897 # all good 898 case Err(why): 899 print(f"couldn't gather all futures, err={why}") 900 ``` 901 902 Parameters 903 ---------- 904 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 905 The async function to call on each item in the iterator. 906 """ 907 return await futures.join(*(func(item) for item in self)) 908 909 def __reversed__(self) -> Iter[Item]: 910 return self.reversed() 911 912 def __repr__(self) -> str: 913 return "<Iterator>" 914 915 def __copy__(self) -> Cloned[Item]: 916 return self.cloned() 917 918 def __deepcopy__( 919 self, memo: collections.MutableMapping[int, typing.Any], / 920 ) -> Copied[Item]: 921 return self.copied() 922 923 def __len__(self) -> int: 924 return self.count() 925 926 def __iter__(self) -> Iterator[Item]: 927 return self
An abstract interface for dealing with iterators.
This is exactly the same trait as core::iter::Iterator trait from Rust.
This is the main interface that any type can implement by basically inheriting from it.
The method __next__ is the only method that needs to be implemented, You get all the other methods for free.
If you want to use a ready iterator for general purposes, Use Iter. This interface is only for implementers
and type hints.
Example
@dataclass
class Counter(Iterator[int]):
start: int = 0
stop: int | None = None
# implement the required method.
def __next__(self) -> int:
result = self.start
self.start += 1
if self.stop is not None and result >= self.stop:
raise StopIteration
return result
counter = Counter(start=0, stop=10)
for i in counter.map(lambda x: x * 2): # multiply each number
...
Implementations
This class implements Iterator in Rust.
175 @staticmethod 176 @typing.final 177 def default() -> Empty[Item]: 178 """Return the default iterator for this type. It returns an empty iterator. 179 180 Example 181 ------- 182 ```py 183 it: Iterator[int] = Iter.default() 184 assert t.next().is_none() 185 ``` 186 """ 187 return Empty()
Return the default iterator for this type. It returns an empty iterator.
Example
it: Iterator[int] = Iter.default()
assert t.next().is_none()
197 @typing.final 198 def collect( 199 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 200 ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]: 201 """Collects all items in the iterator into a sequence. 202 203 Example 204 ------- 205 ```py 206 iterator = Iter(range(3)) 207 iterator.collect() 208 # (0, 1, 2, 3) 209 iterator.collect(cast=str) # Map each element and collect it. 210 # ('0', '1', '2', '3') 211 ``` 212 213 Parameters 214 ---------- 215 cast: `T | None` 216 An optional type to cast the items into. 217 If not provided the items will be returned as it's original type. 218 """ 219 if cast is not None: 220 return list(cast(i) for i in self) 221 222 return list(_ for _ in self)
Collects all items in the iterator into a sequence.
Example
iterator = Iter(range(3))
iterator.collect()
# (0, 1, 2, 3)
iterator.collect(cast=str) # Map each element and collect it.
# ('0', '1', '2', '3')
Parameters
- cast (
T | None): An optional type to cast the items into. If not provided the items will be returned as it's original type.
224 @typing.final 225 def collect_into(self, collection: Collector[Item]) -> None: 226 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 227 228 Example 229 ------- 230 ```py 231 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 232 uniques = set() 233 iterator.collect_into(uniques) 234 # assert uniques == {1, 2, 3, 4, 6} 235 ``` 236 237 Parameters 238 ---------- 239 collection: `MutableSequence[T]` | `set[T]` 240 The collection to extend the items in this iterator with. 241 """ 242 if isinstance(collection, collections.MutableSequence): 243 collection.extend(_ for _ in self) 244 elif isinstance(collection, collections.MutableSet): 245 collection.update(_ for _ in self) 246 else: 247 for idx, item in enumerate(self): 248 collection[idx] = item
Consume this iterator, extending all items in the iterator into a mutable collection.
Example
iterator = Iter([1, 1, 2, 3, 4, 2, 6])
uniques = set()
iterator.collect_into(uniques)
# assert uniques == {1, 2, 3, 4, 6}
Parameters
- collection (
MutableSequence[T]|set[T]): The collection to extend the items in this iterator with.
250 @typing.final 251 def to_vec(self) -> Vec[Item]: 252 """Convert this iterator into `Vec[T]`. 253 254 Example 255 ------- 256 ```py 257 it = sain.iter.once(0) 258 vc = it.to_vec() 259 260 assert to_vec == [0] 261 ``` 262 """ 263 from .collections.vec import Vec 264 265 return Vec(_ for _ in self)
Convert this iterator into Vec[T].
Example
it = sain.iter.once(0)
vc = it.to_vec()
assert to_vec == [0]
267 @typing.final 268 def sink(self) -> None: 269 """Consume all elements from this iterator, flushing it into the sink. 270 271 Example 272 ------- 273 ```py 274 it = Iter((1, 2, 3)) 275 it.sink() 276 assert it.next().is_none() 277 ``` 278 """ 279 for _ in self: 280 pass
Consume all elements from this iterator, flushing it into the sink.
Example
it = Iter((1, 2, 3))
it.sink()
assert it.next().is_none()
282 @typing.final 283 def raw_parts(self) -> collections.Generator[Item, None, None]: 284 """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items. 285 286 This mainly used for objects that needs to satisfy its exact type. 287 288 ```py 289 it = Iter("cba") 290 sort = sorted(it.raw_parts()) 291 292 assert it.count() == 0 293 assert sort == ["a", "b", "c"] 294 ``` 295 """ 296 for item in self: 297 yield item
Decompose this iterator into a Generator[Item] that yields all of the remaining items.
This mainly used for objects that needs to satisfy its exact type.
it = Iter("cba")
sort = sorted(it.raw_parts())
assert it.count() == 0
assert sort == ["a", "b", "c"]
303 def next(self) -> Option[Item]: 304 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 305 306 Example 307 ------- 308 ```py 309 iterator = Iter(["1", "2"]) 310 assert iterator.next() == Some("1") 311 assert iterator.next() == Some("2") 312 assert iterator.next().is_none() 313 ``` 314 """ 315 try: 316 return _option.Some(self.__next__()) 317 except StopIteration: 318 # ! SAFETY: No more items in the iterator. 319 return _option.NOTHING
Advance the iterator, Returning the next item, Some(None) if all items yielded.
Example
iterator = Iter(["1", "2"])
assert iterator.next() == Some("1")
assert iterator.next() == Some("2")
assert iterator.next().is_none()
321 def cloned(self) -> Cloned[Item]: 322 """Creates an iterator which shallow copies its elements by reference. 323 324 If you need a copy of the actual iterator and not the elements. 325 use `Iter.clone()` 326 327 .. note:: 328 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 329 on each item that is being yielded. 330 331 Example 332 ------- 333 ```py 334 @dataclass 335 class User: 336 users_ids: list[int] = [] 337 338 # An iterator which elements points to the same user. 339 user = User() 340 it = Iter((user, user)) 341 342 for u in it.cloned(): 343 u.user_ids.append(1) 344 345 # We iterated over the same user pointer twice and appended "1" 346 # since `copy` returns a shallow copy of nested structures. 347 assert len(user.user_ids) == 2 348 ``` 349 """ 350 return Cloned(self)
Creates an iterator which shallow copies its elements by reference.
If you need a copy of the actual iterator and not the elements.
use Iter.clone()
This method calls copy.copy()
on each item that is being yielded.
Example
@dataclass
class User:
users_ids: list[int] = []
# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))
for u in it.cloned():
u.user_ids.append(1)
# We iterated over the same user pointer twice and appended "1"
# since `copy` returns a shallow copy of nested structures.
assert len(user.user_ids) == 2
352 def copied(self) -> Copied[Item]: 353 """Creates an iterator which copies all of its elements by value. 354 355 If you only need a copy of the item reference, Use `.cloned()` instead. 356 357 .. note:: 358 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 359 on each item that is being yielded. 360 361 Example 362 ------- 363 ```py 364 @dataclass 365 class User: 366 users_ids: list[int] = [] 367 368 # An iterator which elements points to the same user. 369 user = User() 370 it = Iter((user, user)) 371 372 for u in it.copied(): 373 # A new list is created for each item. 374 u.user_ids.append(1) 375 376 # The actual list is untouched since we consumed a deep copy of it. 377 assert len(user.user_ids) == 0 378 ``` 379 """ 380 return Copied(self)
Creates an iterator which copies all of its elements by value.
If you only need a copy of the item reference, Use .cloned() instead.
This method simply calls copy.deepcopy()
on each item that is being yielded.
Example
@dataclass
class User:
users_ids: list[int] = []
# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))
for u in it.copied():
# A new list is created for each item.
u.user_ids.append(1)
# The actual list is untouched since we consumed a deep copy of it.
assert len(user.user_ids) == 0
382 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 383 """Maps each item in the iterator to another type. 384 385 Example 386 ------- 387 ```py 388 iterator = Iter(["1", "2", "3"]).map(int) 389 390 for item in iterator: 391 assert isinstance(item, int) 392 ``` 393 394 Parameters 395 ---------- 396 predicate: `Callable[[Item], OtherItem]` 397 The function to map each item in the iterator to the other type. 398 """ 399 return Map(self, fn)
Maps each item in the iterator to another type.
Example
iterator = Iter(["1", "2", "3"]).map(int)
for item in iterator:
assert isinstance(item, int)
Parameters
- predicate (
Callable[[Item], OtherItem]): The function to map each item in the iterator to the other type.
401 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 402 """Filters the iterator to only yield items that match the predicate. 403 404 Example 405 ------- 406 ```py 407 places = Iter(['London', 'Paris', 'Los Angeles']) 408 for place in places.filter(lambda place: place.startswith('L')): 409 print(place) 410 411 # London 412 # Los Angeles 413 ``` 414 """ 415 return Filter(self, predicate)
Filters the iterator to only yield items that match the predicate.
Example
places = Iter(['London', 'Paris', 'Los Angeles'])
for place in places.filter(lambda place: place.startswith('L')):
print(place)
# London
# Los Angeles
417 def take(self, count: int) -> Take[Item]: 418 """Take the first number of items until the number of items 419 are yielded or the end of the iterator is exhausted. 420 421 Example 422 ------- 423 ```py 424 iterator = Iter(['c', 'x', 'y']) 425 426 for x in iterator.take(2): 427 assert x in ('c', 'x') 428 429 # <Iter(['c', 'x'])> 430 ``` 431 """ 432 return Take(self, count)
Take the first number of items until the number of items are yielded or the end of the iterator is exhausted.
Example
iterator = Iter(['c', 'x', 'y'])
for x in iterator.take(2):
assert x in ('c', 'x')
# <Iter(['c', 'x'])>
434 def skip(self, count: int) -> Skip[Item]: 435 """Skips the first number of items in the iterator. 436 437 Example 438 ------- 439 ```py 440 iterator = Iter((1, 2, 3, 4)) 441 for i in iterator.skip(2): 442 print(i) 443 444 # 3 445 # 4 446 ``` 447 """ 448 return Skip(self, count)
Skips the first number of items in the iterator.
Example
iterator = Iter((1, 2, 3, 4))
for i in iterator.skip(2):
print(i)
# 3
# 4
450 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 451 """Create a new iterator that yields a tuple of the index and item. 452 453 Example 454 ------- 455 ```py 456 iterator = Iter([1, 2, 3]) 457 for index, item in iterator.enumerate(): 458 print(index, item) 459 460 # 0 1 461 # 1 2 462 # 2 3 463 ``` 464 """ 465 return Enumerate(self, start)
Create a new iterator that yields a tuple of the index and item.
Example
iterator = Iter([1, 2, 3])
for index, item in iterator.enumerate():
print(index, item)
# 0 1
# 1 2
# 2 3
467 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 468 """yields items from the iterator while predicate returns `True`. 469 470 The rest of the items are discarded as soon as the predicate returns `False` 471 472 Example 473 ------- 474 ```py 475 iterator = Iter(['a', 'ab', 'xd', 'ba']) 476 for x in iterator.take_while(lambda x: 'a' in x): 477 print(x) 478 479 # a 480 # ab 481 ``` 482 483 Parameters 484 ---------- 485 predicate: `collections.Callable[[Item], bool]` 486 The function to predicate each item in the iterator. 487 """ 488 return TakeWhile(self, f)
yields items from the iterator while predicate returns True.
The rest of the items are discarded as soon as the predicate returns False
Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.take_while(lambda x: 'a' in x):
print(x)
# a
# ab
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
490 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 491 """Yields items from the iterator while predicate returns `False`. 492 493 Example 494 ------- 495 ```py 496 iterator = Iter(['a', 'ab', 'xd', 'ba']) 497 for x in iterator.drop_while(lambda x: 'a' in x): 498 print(x) 499 500 # xd 501 # ba 502 ``` 503 504 Parameters 505 ---------- 506 predicate: `collections.Callable[[Item], bool]` 507 The function to predicate each item in the iterator. 508 """ 509 return DropWhile(self, f)
Yields items from the iterator while predicate returns False.
Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.drop_while(lambda x: 'a' in x):
print(x)
# xd
# ba
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
511 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 512 """Returns an iterator over `chunk_size` elements of the iterator at a time, 513 starting at the beginning of the iterator. 514 515 Example 516 ------- 517 ```py 518 iter = Iter(['a', 'b', 'c', 'd', 'e']) 519 chunks = iter.chunks() 520 assert chunks.next().unwrap() == ['a', 'b'] 521 assert chunks.next().unwrap() == ['c', 'd'] 522 assert chunks.next().unwrap() == ['e'] 523 assert chunks.next().is_none() 524 ``` 525 """ 526 return Chunks(self, chunk_size)
Returns an iterator over chunk_size elements of the iterator at a time,
starting at the beginning of the iterator.
Example
iter = Iter(['a', 'b', 'c', 'd', 'e'])
chunks = iter.chunks()
assert chunks.next().unwrap() == ['a', 'b']
assert chunks.next().unwrap() == ['c', 'd']
assert chunks.next().unwrap() == ['e']
assert chunks.next().is_none()
528 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 529 """Return `True` if all items in the iterator match the predicate. 530 531 Example 532 ------- 533 ```py 534 iterator = Iter([1, 2, 3]) 535 while iterator.all(lambda item: isinstance(item, int)): 536 print("Still all integers") 537 continue 538 # Still all integers 539 ``` 540 541 Parameters 542 ---------- 543 predicate: `collections.Callable[[Item], bool]` 544 The function to test each item in the iterator. 545 """ 546 return all(predicate(item) for item in self)
Return True if all items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
print("Still all integers")
continue
# Still all integers
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to test each item in the iterator.
548 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 549 """`True` if any items in the iterator match the predicate. 550 551 Example 552 ------- 553 ```py 554 iterator = Iter([1, 2, 3]) 555 if iterator.any(lambda item: isinstance(item, int)): 556 print("At least one item is an int.") 557 # At least one item is an int. 558 ``` 559 560 Parameters 561 ---------- 562 predicate: `collections.Callable[[Item], bool]` 563 The function to test each item in the iterator. 564 """ 565 return any(predicate(item) for item in self)
True if any items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
print("At least one item is an int.")
# At least one item is an int.
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to test each item in the iterator.
567 def zip( 568 self, other: collections.Iterable[OtherItem] 569 ) -> Iter[tuple[Item, OtherItem]]: 570 """Zips the iterator with another iterable. 571 572 Example 573 ------- 574 ```py 575 iterator = Iter([1, 2, 3]) 576 for item, other_item in iterator.zip([4, 5, 6]): 577 assert item == other_item 578 <Iter([(1, 4), (2, 5), (3, 6)])> 579 ``` 580 581 Parameters 582 ---------- 583 other: `Iter[OtherItem]` 584 The iterable to zip with. 585 586 Returns 587 ------- 588 `Iter[tuple[Item, OtherItem]]` 589 The zipped iterator. 590 591 """ 592 return Iter(zip(self.raw_parts(), other))
Zips the iterator with another iterable.
Example
iterator = Iter([1, 2, 3])
for item, other_item in iterator.zip([4, 5, 6]):
assert item == other_item
<Iter([(1, 4), (2, 5), (3, 6)])>
Parameters
- other (
Iter[OtherItem]): The iterable to zip with.
Returns
Iter[tuple[Item, OtherItem]]: The zipped iterator.
594 def sort( 595 self, 596 *, 597 key: collections.Callable[[Item], _typeshed.SupportsRichComparison], 598 reverse: bool = False, 599 ) -> Iter[Item]: 600 """Sorts the iterator. 601 602 Example 603 ------- 604 ```py 605 iterator = Iter([3, 1, 6, 7]) 606 for item in iterator.sort(key=lambda item: item < 3): 607 print(item) 608 # 1 609 # 3 610 # 6 611 # 7 612 ``` 613 614 Parameters 615 ---------- 616 key: `collections.Callable[[Item], Any]` 617 The function to sort by. 618 reverse: `bool` 619 Whether to reverse the sort. 620 """ 621 return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))
Sorts the iterator.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.sort(key=lambda item: item < 3):
print(item)
# 1
# 3
# 6
# 7
Parameters
- key (
collections.Callable[[Item], Any]): The function to sort by. - reverse (
bool): Whether to reverse the sort.
623 def reversed(self) -> Iter[Item]: 624 """Returns a new iterator that yields the items in the iterator in reverse order. 625 626 This consumes this iterator into a sequence and return a new iterator containing all of the elements 627 in reversed order. 628 629 Example 630 ------- 631 ```py 632 iterator = Iter([3, 1, 6, 7]) 633 for item in iterator.reversed(): 634 print(item) 635 # 7 636 # 6 637 # 1 638 # 3 639 ``` 640 """ 641 # NOTE: In order to reverse the iterator we need to 642 # first collect it into some collection. 643 return Iter(reversed(list(_ for _ in self)))
Returns a new iterator that yields the items in the iterator in reverse order.
This consumes this iterator into a sequence and return a new iterator containing all of the elements in reversed order.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.reversed():
print(item)
# 7
# 6
# 1
# 3
645 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 646 """Returns a new iterator that yields all items from both iterators. 647 648 Example 649 ------- 650 ```py 651 iterator = Iter([1, 2, 3]) 652 other = [4, 5, 6] 653 654 for item in iterator.union(other): 655 print(item) 656 # 1 657 # 2 658 # 3 659 # 4 660 # 5 661 # 6 662 ``` 663 664 Parameters 665 ---------- 666 other: `Iter[Item]` 667 The iterable to union with. 668 """ 669 return Iter(itertools.chain(self.raw_parts(), other))
Returns a new iterator that yields all items from both iterators.
Example
iterator = Iter([1, 2, 3])
other = [4, 5, 6]
for item in iterator.union(other):
print(item)
# 1
# 2
# 3
# 4
# 5
# 6
Parameters
- other (
Iter[Item]): The iterable to union with.
671 def first(self) -> Option[Item]: 672 """Returns the first item in the iterator. 673 674 Example 675 ------- 676 ```py 677 iterator = Iter([3, 1, 6, 7]) 678 iterator.first().is_some_and(lambda x: x == 3) 679 ``` 680 """ 681 return self.take(1).next()
Returns the first item in the iterator.
Example
iterator = Iter([3, 1, 6, 7])
iterator.first().is_some_and(lambda x: x == 3)
683 def last(self) -> Option[Item]: 684 """Returns the last item in the iterator. 685 686 Example 687 ------- 688 ```py 689 iterator = Iter([3, 1, 6, 7]) 690 iterator.last().is_some_and(lambda x: x == 7) 691 ``` 692 """ 693 return self.reversed().first()
Returns the last item in the iterator.
Example
iterator = Iter([3, 1, 6, 7])
iterator.last().is_some_and(lambda x: x == 7)
695 def count(self) -> int: 696 """Return the count of elements in memory this iterator has. 697 698 Example 699 ------- 700 ```py 701 it = Iter(range(3)) 702 assert it.count() == 3 703 ``` 704 """ 705 count = 0 706 for _ in self: 707 count += 1 708 709 return count
Return the count of elements in memory this iterator has.
Example
it = Iter(range(3))
assert it.count() == 3
711 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 712 """Searches for an element of an iterator that satisfies a predicate. 713 714 If you want the position of the element, use `Iterator.position` instead. 715 716 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 717 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 718 719 Example 720 ------- 721 ```py 722 it = Iter(range(10)) 723 item = it.find(lambda num: num > 5) 724 print(item) # 6 725 ``` 726 """ 727 for item in self: 728 if predicate(item): 729 return _option.Some(item) 730 731 # no more items 732 return _option.NOTHING
Searches for an element of an iterator that satisfies a predicate.
If you want the position of the element, use Iterator.position instead.
find() takes a lambda that returns true or false. It applies this closure to each element of the iterator,
and if any of them return true, then find() returns Some(element). If they all return false, it returns None.
Example
it = Iter(range(10))
item = it.find(lambda num: num > 5)
print(item) # 6
734 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 735 """Searches for the position of an element in the iterator that satisfies a predicate. 736 737 If you want the object itself, use `Iterator.find` instead. 738 739 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 740 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 741 742 Example 743 ------- 744 ```py 745 it = Iter(range(10)) 746 position = it.find(lambda num: num > 5) 747 assert position.unwrap() == 6 748 ``` 749 """ 750 for position, value in self.enumerate(): 751 if predicate(value): 752 return _option.Some(position) 753 754 # no more items 755 return _option.NOTHING
Searches for the position of an element in the iterator that satisfies a predicate.
If you want the object itself, use Iterator.find instead.
position() takes a lambda that returns true or false. It applies this closure to each element of the iterator,
and if any of them return true, then position() returns Some(position_of_element). If they all return false, it returns None.
Example
it = Iter(range(10))
position = it.find(lambda num: num > 5)
assert position.unwrap() == 6
757 def fold( 758 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 759 ) -> OtherItem: 760 """Folds every element into an accumulator by applying an operation, returning the final result. 761 762 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 763 The closure returns the value that the accumulator should have for the next iteration. 764 765 The initial value is the value the accumulator will have on the first call. 766 767 After applying this closure to every element of the iterator, fold() returns the accumulator. 768 769 This operation is sometimes called ‘reduce’ or ‘inject’. 770 771 Example 772 ------- 773 ```py 774 a = Iter([1, 2, 3, 4]) 775 sum = a.fold(0, lambda acc, elem: acc + elem) 776 assert sum == 10 777 ``` 778 """ 779 accum = init 780 while True: 781 try: 782 x = self.__next__() 783 accum = f(accum, x) 784 except StopIteration: 785 break 786 787 return accum
Folds every element into an accumulator by applying an operation, returning the final result.
fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. The closure returns the value that the accumulator should have for the next iteration.
The initial value is the value the accumulator will have on the first call.
After applying this closure to every element of the iterator, fold() returns the accumulator.
This operation is sometimes called ‘reduce’ or ‘inject’.
Example
a = Iter([1, 2, 3, 4])
sum = a.fold(0, lambda acc, elem: acc + elem)
assert sum == 10
789 def advance_by(self, n: int) -> _result.Result[None, int]: 790 """Advances the iterator by `n` elements. 791 792 Returns `Result[None, int]`, where `Ok(None)` means the iterator 793 advanced successfully, and `Err(int)` if `None` encountered, where `int` 794 represents the remaining number of steps that could not be advanced because the iterator ran out. 795 796 Example 797 ------- 798 ```py 799 it = into_iter([1, 2, 3, 4]) 800 assert it.advance_by(2).is_ok() 801 assert it.next() == Some(3) 802 assert it.advance_by(0).is_ok() 803 assert it.advance_by(100) == Err(99) 804 ``` 805 """ 806 for i in range(n): 807 try: 808 self.__next__() 809 except StopIteration: 810 return _result.Err(n - i) 811 812 return _result.Ok(None)
Advances the iterator by n elements.
Returns Result[None, int], where Ok(None) means the iterator
advanced successfully, and Err(int) if None encountered, where int
represents the remaining number of steps that could not be advanced because the iterator ran out.
Example
it = into_iter([1, 2, 3, 4])
assert it.advance_by(2).is_ok()
assert it.next() == Some(3)
assert it.advance_by(0).is_ok()
assert it.advance_by(100) == Err(99)
814 def nth(self, n: int) -> Option[Item]: 815 """Returns the `n`th element of the iterator 816 817 Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first 818 value. 819 820 Note all elements before `n` will be skipped / consumed. 821 822 Example 823 ------- 824 ```py 825 a = into_iter([1, 2, 3]) 826 assert a.iter().nth(1) == Some(2) 827 ``` 828 """ 829 for _ in range(n): 830 try: 831 self.__next__() 832 except StopIteration: 833 return _option.NOTHING 834 835 return self.next()
Returns the nth element of the iterator
Just like normal indexing, the count n starts from zero, so nth(0) returns the first
value.
Note all elements before n will be skipped / consumed.
Example
a = into_iter([1, 2, 3])
assert a.iter().nth(1) == Some(2)
837 def sum(self: Sum) -> int: 838 """Sums an iterator of a possible type `T` that can be converted to an integer. 839 840 where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`). 841 842 Example 843 ------- 844 ```py 845 numbers: Iterator[str] = Iter(["1", "2", "3"]) 846 total = numbers.sum() 847 assert total == 6 848 ``` 849 """ 850 return sum(int(_) for _ in self)
Sums an iterator of a possible type T that can be converted to an integer.
where T is a typeof (int, float, str, ReadableBuffer, SupportsTrunc, SupportsIndex).
Example
numbers: Iterator[str] = Iter(["1", "2", "3"])
total = numbers.sum()
assert total == 6
852 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 853 """Calls `func` on each item in the iterator. 854 855 Example 856 ------- 857 ```py 858 iterator = Iter([1, 2, 3]) 859 iterator.for_each(lambda item: print(item)) 860 # 1 861 # 2 862 # 3 863 ``` 864 865 Parameters 866 ---------- 867 func: `collections.Callable[[Item], typing.Any]` 868 The function to call on each item in the iterator. 869 """ 870 for item in self: 871 func(item)
Calls func on each item in the iterator.
Example
iterator = Iter([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
- func (
collections.Callable[[Item], typing.Any]): The function to call on each item in the iterator.
873 async def async_for_each( 874 self, 875 func: collections.Callable[ 876 [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem] 877 ], 878 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 879 """Calls the async function on each item in the iterator *concurrently*. 880 881 Concurrently meaning that the next item will not wait for other items 882 to finish to execute, each item gets called in a separate task. 883 884 After all the tasks finish, a `Result[list[T], JoinError]` will be returned, 885 which will need to be handled by the caller. 886 887 Example 888 ------- 889 ```py 890 async def create_user(username: str) -> None: 891 await aiohttp.request("POST", f'.../{username}') 892 893 async def main(): 894 users = sain.into_iter(["Danny", "Flower"]) 895 match await users.async_for_each(lambda username: create_user(username)): 896 case Ok(result): 897 # all good 898 case Err(why): 899 print(f"couldn't gather all futures, err={why}") 900 ``` 901 902 Parameters 903 ---------- 904 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 905 The async function to call on each item in the iterator. 906 """ 907 return await futures.join(*(func(item) for item in self))
Calls the async function on each item in the iterator concurrently.
Concurrently meaning that the next item will not wait for other items to finish to execute, each item gets called in a separate task.
After all the tasks finish, a Result[list[T], JoinError] will be returned,
which will need to be handled by the caller.
Example
async def create_user(username: str) -> None:
await aiohttp.request("POST", f'.../{username}')
async def main():
users = sain.into_iter(["Danny", "Flower"])
match await users.async_for_each(lambda username: create_user(username)):
case Ok(result):
# all good
case Err(why):
print(f"couldn't gather all futures, err={why}")
Parameters
- func (
collections.Callable[[Item], Coroutine[None, Any, Any]]): The async function to call on each item in the iterator.
1070@typing.final 1071class TrustedIter(typing.Generic[Item], ExactSizeIterator[Item]): 1072 """Similar to `Iter`, but it reports an accurate length using `ExactSizeIterator`. 1073 1074 iterable objects such as `Vec`, `Bytes`, `list` and other `Sized` may be created 1075 using this iterator. 1076 1077 Example 1078 ------- 1079 ```py 1080 # we know the size of the iterator. 1081 sized_buf: TrustedIter[int] = into_iter((1, 2, 3, 4)) 1082 # this is `Iter[int]` since we don't know when the generator will stop yielding. 1083 unsized_buf: Iter[int] = into_iter((_ for _ in ([1, 2, 3, 4] if cond else [1, 2]))) 1084 ``` 1085 1086 Parameters 1087 ---------- 1088 items: `collections.Collection[Item]` 1089 A sized collection of items to iterate over. 1090 """ 1091 1092 __slots__ = ("_it", "_len", "__alive") 1093 1094 def __init__(self, iterable: collections.Sequence[Item]) -> None: 1095 self.__alive = iterable 1096 self._len = len(iterable) 1097 self._it = iter(iterable) 1098 1099 @property 1100 def __slice_checked_get(self) -> collections.Sequence[Item] | None: 1101 try: 1102 return self.__alive 1103 except AttributeError: 1104 return None 1105 1106 def next(self) -> Option[Item]: 1107 if self._len == 0: 1108 # ! SAFETY: len == 0 1109 return _option.NOTHING 1110 1111 return _option.Some(self.__next__()) 1112 1113 @unsafe 1114 def next_unchecked(self) -> Item: 1115 """Returns the next item in the iterator without checking if it exists. 1116 1117 This is equivalent to calling `next()` on the iterator directly. 1118 1119 Example 1120 ------- 1121 ```py 1122 iterator = Iter([1]) 1123 assert iterator.next_unchecked() == 1 1124 iterator.next_unchecked() # raises StopIteration 1125 ``` 1126 """ 1127 return self.__next__() 1128 1129 @unsafe 1130 def set_len(self, new_len: int) -> None: 1131 """Sets the length of the iterator to `new_len`. 1132 1133 This is unsafe and should only be used if you know what you're doing. 1134 1135 Example 1136 ------- 1137 ```py 1138 iterator = Iter([1, 2, 3]) 1139 iterator.set_len(2) 1140 assert iterator.len() == 2 1141 ``` 1142 """ 1143 self._len = new_len 1144 1145 def as_slice(self) -> Slice[Item]: 1146 """Returns an immutable slice of all elements that have not been yielded 1147 1148 Example 1149 ------- 1150 ```py 1151 iterator = into_iter([1, 2, 3]) 1152 iterator.as_slice() == [1, 2, 3] 1153 iterator.next() 1154 assert iterator.as_slice() == [2, 3] 1155 ``` 1156 """ 1157 from .collections.slice import Slice 1158 1159 return Slice(self.__slice_checked_get or ()) 1160 1161 def __repr__(self) -> str: 1162 # __alive is dropped from `self`. 1163 if (s := self.__slice_checked_get) is None: 1164 return "TrustedIter(<empty>)" 1165 1166 return f"TrustedIter({s[-self._len :]})" 1167 1168 def __next__(self) -> Item: 1169 try: 1170 i = next(self._it) 1171 except StopIteration: 1172 # don't reference this anymore. 1173 del self.__alive 1174 raise 1175 1176 self._len -= 1 1177 return i 1178 1179 def __getitem__(self, index: int) -> Item: 1180 if self._len == 0: 1181 raise IndexError("index out of bounds") 1182 1183 return self.skip(index).first().unwrap_or_else(oob) 1184 1185 def __contains__(self, item: Item) -> bool: 1186 return item in self._it 1187 1188 def __len__(self) -> int: 1189 return self._len
Similar to Iter, but it reports an accurate length using ExactSizeIterator.
iterable objects such as Vec, Bytes, list and other Sized may be created
using this iterator.
Example
# we know the size of the iterator.
sized_buf: TrustedIter[int] = into_iter((1, 2, 3, 4))
# this is `Iter[int]` since we don't know when the generator will stop yielding.
unsized_buf: Iter[int] = into_iter((_ for _ in ([1, 2, 3, 4] if cond else [1, 2])))
Parameters
- items (
collections.Collection[Item]): A sized collection of items to iterate over.
1106 def next(self) -> Option[Item]: 1107 if self._len == 0: 1108 # ! SAFETY: len == 0 1109 return _option.NOTHING 1110 1111 return _option.Some(self.__next__())
Advance the iterator, Returning the next item, Some(None) if all items yielded.
Example
iterator = Iter(["1", "2"])
assert iterator.next() == Some("1")
assert iterator.next() == Some("2")
assert iterator.next().is_none()
1113 @unsafe 1114 def next_unchecked(self) -> Item: 1115 """Returns the next item in the iterator without checking if it exists. 1116 1117 This is equivalent to calling `next()` on the iterator directly. 1118 1119 Example 1120 ------- 1121 ```py 1122 iterator = Iter([1]) 1123 assert iterator.next_unchecked() == 1 1124 iterator.next_unchecked() # raises StopIteration 1125 ``` 1126 """ 1127 return self.__next__()
Returns the next item in the iterator without checking if it exists.
This is equivalent to calling next() on the iterator directly.
Example
iterator = Iter([1])
assert iterator.next_unchecked() == 1
iterator.next_unchecked() # raises StopIteration
Safety ⚠️
Calling this method without knowing the output is considered undefined behavior.
1129 @unsafe 1130 def set_len(self, new_len: int) -> None: 1131 """Sets the length of the iterator to `new_len`. 1132 1133 This is unsafe and should only be used if you know what you're doing. 1134 1135 Example 1136 ------- 1137 ```py 1138 iterator = Iter([1, 2, 3]) 1139 iterator.set_len(2) 1140 assert iterator.len() == 2 1141 ``` 1142 """ 1143 self._len = new_len
Sets the length of the iterator to new_len.
This is unsafe and should only be used if you know what you're doing.
Example
iterator = Iter([1, 2, 3])
iterator.set_len(2)
assert iterator.len() == 2
Safety ⚠️
Calling this method without knowing the output is considered undefined behavior.
1145 def as_slice(self) -> Slice[Item]: 1146 """Returns an immutable slice of all elements that have not been yielded 1147 1148 Example 1149 ------- 1150 ```py 1151 iterator = into_iter([1, 2, 3]) 1152 iterator.as_slice() == [1, 2, 3] 1153 iterator.next() 1154 assert iterator.as_slice() == [2, 3] 1155 ``` 1156 """ 1157 from .collections.slice import Slice 1158 1159 return Slice(self.__slice_checked_get or ())
Returns an immutable slice of all elements that have not been yielded
Example
iterator = into_iter([1, 2, 3])
iterator.as_slice() == [1, 2, 3]
iterator.next()
assert iterator.as_slice() == [2, 3]
1192@diagnostic 1193class Cloned(typing.Generic[Item], Iterator[Item]): 1194 """An iterator that copies the elements from an underlying iterator. 1195 1196 This iterator is created by the `Iterator.cloned` method. 1197 """ 1198 1199 __slots__ = ("_it",) 1200 1201 def __init__(self, it: Iterator[Item]) -> None: 1202 self._it = it 1203 1204 def __next__(self) -> Item: 1205 n = self._it.__next__() 1206 1207 # Avoid useless function call for a list. 1208 if isinstance(n, list): 1209 # SAFETY: We know this is a list. 1210 return n[:] # pyright: ignore 1211 1212 return copy.copy(n)
An iterator that copies the elements from an underlying iterator.
This iterator is created by the Iterator.cloned method.
1215@diagnostic 1216class Copied(typing.Generic[Item], Iterator[Item]): 1217 """An iterator that deeply-copies the elements from an underlying iterator. 1218 1219 This iterator is created by the `Iterator.copied` method. 1220 """ 1221 1222 __slots__ = ("_it",) 1223 1224 def __init__(self, it: Iterator[Item]) -> None: 1225 self._it = it 1226 1227 def __next__(self) -> Item: 1228 return copy.deepcopy(self._it.__next__())
An iterator that deeply-copies the elements from an underlying iterator.
This iterator is created by the Iterator.copied method.
1273@diagnostic 1274class Take(typing.Generic[Item], Iterator[Item]): 1275 """An iterator that yields the first `number` of elements and drops the rest. 1276 1277 This iterator is created by the `Iterator.take` method. 1278 """ 1279 1280 __slots__ = ("_it", "_taken", "_count") 1281 1282 def __init__(self, it: Iterator[Item], count: int) -> None: 1283 if count <= 0: 1284 raise ValueError("`count` must be non-zero") 1285 1286 self._it = it 1287 self._taken = count 1288 self._count = 0 1289 1290 def __next__(self) -> Item: 1291 if self._count >= self._taken: 1292 unreachable() 1293 1294 item = self._it.__next__() 1295 self._count += 1 1296 return item
An iterator that yields the first number of elements and drops the rest.
This iterator is created by the Iterator.take method.
1250@diagnostic 1251class Filter(typing.Generic[Item], Iterator[Item]): 1252 """An iterator that filters the elements to a `predicate`. 1253 1254 This iterator is created by the `Iterator.filter` method. 1255 """ 1256 1257 __slots__ = ("_it", "_call") 1258 1259 def __init__( 1260 self, it: Iterator[Item], call: collections.Callable[[Item], bool] 1261 ) -> None: 1262 self._it = it 1263 self._call = call 1264 1265 def __next__(self) -> Item: 1266 for item in self._it: 1267 if self._call(item): 1268 return item 1269 1270 unreachable()
An iterator that filters the elements to a predicate.
This iterator is created by the Iterator.filter method.
1231@diagnostic 1232class Map(typing.Generic[Item, OtherItem], Iterator[OtherItem]): 1233 """An iterator that maps the elements to a callable. 1234 1235 This iterator is created by the `Iterator.map` method. 1236 """ 1237 1238 __slots__ = ("_it", "_call") 1239 1240 def __init__( 1241 self, it: Iterator[Item], call: collections.Callable[[Item], OtherItem] 1242 ) -> None: 1243 self._it = it 1244 self._call = call 1245 1246 def __next__(self) -> OtherItem: 1247 return self._call(self._it.__next__())
An iterator that maps the elements to a callable.
This iterator is created by the Iterator.map method.
1299@diagnostic 1300class Skip(typing.Generic[Item], Iterator[Item]): 1301 """An iterator that skips the first `number` of elements and yields the rest. 1302 1303 This iterator is created by the `Iterator.skip` method. 1304 """ 1305 1306 __slots__ = ("_it", "_count", "_skipped") 1307 1308 def __init__(self, it: Iterator[Item], count: int) -> None: 1309 if count <= 0: 1310 raise ValueError("`count` must be non-zero") 1311 1312 self._it = it 1313 self._count = count 1314 self._skipped = 0 1315 1316 def __next__(self) -> Item: 1317 while self._skipped < self._count: 1318 self._skipped += 1 1319 self._it.__next__() 1320 1321 return self._it.__next__()
An iterator that skips the first number of elements and yields the rest.
This iterator is created by the Iterator.skip method.
1324@diagnostic 1325class Enumerate(typing.Generic[Item], Iterator[tuple[int, Item]]): 1326 """An iterator that yields the current count and the element during iteration. 1327 1328 This iterator is created by the `Iterator.enumerate` method. 1329 """ 1330 1331 __slots__ = ("_it", "_count") 1332 1333 def __init__(self, it: Iterator[Item], start: int) -> None: 1334 self._it = it 1335 self._count = start 1336 1337 def __next__(self) -> tuple[int, Item]: 1338 a = self._it.__next__() 1339 i = self._count 1340 self._count += 1 1341 return i, a
An iterator that yields the current count and the element during iteration.
This iterator is created by the Iterator.enumerate method.
1344@diagnostic 1345class TakeWhile(typing.Generic[Item], Iterator[Item]): 1346 """An iterator that yields elements while `predicate` returns `True`. 1347 1348 This iterator is created by the `Iterator.take_while` method. 1349 """ 1350 1351 __slots__ = ("_it", "_predicate") 1352 1353 def __init__( 1354 self, it: Iterator[Item], predicate: collections.Callable[[Item], bool] 1355 ) -> None: 1356 self._it = it 1357 self._predicate = predicate 1358 1359 def __next__(self) -> Item: 1360 item = self._it.__next__() 1361 1362 if self._predicate(item): 1363 return item 1364 1365 unreachable()
An iterator that yields elements while predicate returns True.
This iterator is created by the Iterator.take_while method.
1368@diagnostic 1369class DropWhile(typing.Generic[Item], Iterator[Item]): 1370 """An iterator that yields elements while `predicate` returns `False`. 1371 1372 This iterator is created by the `Iterator.drop_while` method. 1373 """ 1374 1375 __slots__ = ("_it", "_predicate", "_dropped") 1376 1377 def __init__( 1378 self, it: Iterator[Item], predicate: collections.Callable[[Item], bool] 1379 ) -> None: 1380 self._it = it 1381 self._predicate = predicate 1382 self._dropped = False 1383 1384 def __next__(self) -> Item: 1385 if not self._dropped: 1386 while not self._predicate(item := self._it.__next__()): 1387 pass 1388 1389 self._dropped = True 1390 return item 1391 1392 unreachable()
An iterator that yields elements while predicate returns False.
This iterator is created by the Iterator.drop_while method.
1395@diagnostic 1396class Chunks(typing.Generic[Item], Iterator[collections.Sequence[Item]]): 1397 """An iterator that yields elements in chunks. 1398 1399 This iterator is created by the `Iterator.chunks` method. 1400 """ 1401 1402 __slots__ = ("chunk_size", "_it") 1403 1404 def __init__(self, it: Iterator[Item], chunk_size: int) -> None: 1405 self.chunk_size = chunk_size 1406 self._it = it 1407 1408 def __next__(self) -> collections.Sequence[Item]: 1409 chunk: list[Item] = [] 1410 1411 for item in self._it: 1412 chunk.append(item) 1413 1414 if len(chunk) == self.chunk_size: 1415 break 1416 1417 if chunk: 1418 return chunk 1419 1420 unreachable()
An iterator that yields elements in chunks.
This iterator is created by the Iterator.chunks method.
1423@typing.final 1424@diagnostic 1425class Empty(typing.Generic[Item], ExactSizeIterator[Item]): 1426 """An iterator that yields nothing. 1427 1428 This is the default iterator that is created by `Iterator.default()` or `empty()` 1429 """ 1430 1431 __slots__ = () 1432 1433 def __init__(self) -> None: 1434 pass 1435 1436 def next(self) -> Option[Item]: 1437 # SAFETY: an empty iterator always returns None. 1438 # also we avoid calling `nothing_unchecked()` here for fast returns. 1439 return _option.NOTHING 1440 1441 def __len__(self) -> typing.Literal[0]: 1442 return 0 1443 1444 def any( 1445 self, predicate: collections.Callable[[Item], bool] 1446 ) -> typing.Literal[False]: 1447 return False 1448 1449 def all( 1450 self, predicate: collections.Callable[[Item], bool] 1451 ) -> typing.Literal[False]: 1452 return False 1453 1454 def __next__(self) -> Item: 1455 unreachable()
An iterator that yields nothing.
This is the default iterator that is created by Iterator.default() or empty()
1436 def next(self) -> Option[Item]: 1437 # SAFETY: an empty iterator always returns None. 1438 # also we avoid calling `nothing_unchecked()` here for fast returns. 1439 return _option.NOTHING
Advance the iterator, Returning the next item, Some(None) if all items yielded.
Example
iterator = Iter(["1", "2"])
assert iterator.next() == Some("1")
assert iterator.next() == Some("2")
assert iterator.next().is_none()
1444 def any( 1445 self, predicate: collections.Callable[[Item], bool] 1446 ) -> typing.Literal[False]: 1447 return False
True if any items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
print("At least one item is an int.")
# At least one item is an int.
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to test each item in the iterator.
1449 def all( 1450 self, predicate: collections.Callable[[Item], bool] 1451 ) -> typing.Literal[False]: 1452 return False
Return True if all items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
print("Still all integers")
continue
# Still all integers
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to test each item in the iterator.
1458@typing.final 1459@diagnostic 1460class Repeat(typing.Generic[Item], ExactSizeIterator[Item]): 1461 """An iterator that repeats a given value an exact number of times. 1462 1463 This iterator is created by calling `repeat()`. 1464 """ 1465 1466 __slots__ = ("_count", "_element") 1467 1468 def __init__(self, element: Item, count: int) -> None: 1469 self._count = count 1470 self._element = element 1471 1472 def __next__(self) -> Item: 1473 if self._count > 0: 1474 self._count -= 1 1475 if self._count == 0: 1476 # Return the origin element last 1477 return self._element 1478 1479 return copy.copy(self._element) 1480 1481 unreachable() 1482 1483 def __len__(self) -> int: 1484 return self._count
An iterator that repeats a given value an exact number of times.
This iterator is created by calling repeat().
1487@typing.final 1488@diagnostic 1489class Once(typing.Generic[Item], ExactSizeIterator[Item]): 1490 """An iterator that yields exactly one item. 1491 1492 This iterator is created by calling `once()`. 1493 """ 1494 1495 __slots__ = ("_item",) 1496 1497 def __init__(self, item: Item) -> None: 1498 self._item: Item | None = item 1499 1500 def __next__(self) -> Item: 1501 if self._item is None: 1502 unreachable() 1503 1504 i = self._item 1505 self._item = None 1506 return i 1507 1508 def __len__(self) -> int: 1509 return 1 if self._item is not None else 0
An iterator that yields exactly one item.
This iterator is created by calling once().
930class ExactSizeIterator(typing.Generic[Item], Iterator[Item], abc.ABC): 931 """An iterator that knows its exact size. 932 933 The implementations of this interface indicates that the iterator knows exactly 934 how many items it can yield. 935 936 however, this is not a requirement for the iterator to implement this trait, as its 937 only used for iterators that can know their size. 938 939 Example 940 ------- 941 ```py 942 @dataclass 943 class Letters(ExactSizeIterator[str]): 944 letters: list[str] 945 946 def __next__(self) -> str: 947 return self.letters.pop(0) 948 949 def __len__(self) -> int: 950 return len(self.letters) 951 952 letters = Letters(['a', 'b', 'c']) 953 assert letters.count() == 3 954 assert letters.next() == Some('a') 955 assert letters.count() == 2 956 ``` 957 """ 958 959 __slots__ = () 960 961 @typing.final 962 def count(self) -> int: 963 return len(self) 964 965 @typing.final 966 def len(self) -> int: 967 """Returns the remaining number of items in the iterator. 968 969 This doesn't exhaust the iterator. 970 971 Example 972 ------- 973 ```py 974 it = once(0) 975 assert it.len() == 1 976 assert it.len() == 1 977 it.next() 978 assert it.len() == 0 979 ``` 980 """ 981 return len(self) 982 983 @typing.final 984 def is_empty(self) -> bool: 985 """Return `True` if this iterator has no items left to yield. 986 987 Example 988 ------- 989 ```py 990 iterator = once(1) 991 assert not iterator.is_empty() 992 assert once.next() == Some(1) 993 assert iterator.is_empty() 994 ``` 995 """ 996 return len(self) == 0 997 998 @abc.abstractmethod 999 def __len__(self) -> int: ...
An iterator that knows its exact size.
The implementations of this interface indicates that the iterator knows exactly how many items it can yield.
however, this is not a requirement for the iterator to implement this trait, as its only used for iterators that can know their size.
Example
@dataclass
class Letters(ExactSizeIterator[str]):
letters: list[str]
def __next__(self) -> str:
return self.letters.pop(0)
def __len__(self) -> int:
return len(self.letters)
letters = Letters(['a', 'b', 'c'])
assert letters.count() == 3
assert letters.next() == Some('a')
assert letters.count() == 2
Return the count of elements in memory this iterator has.
Example
it = Iter(range(3))
assert it.count() == 3
965 @typing.final 966 def len(self) -> int: 967 """Returns the remaining number of items in the iterator. 968 969 This doesn't exhaust the iterator. 970 971 Example 972 ------- 973 ```py 974 it = once(0) 975 assert it.len() == 1 976 assert it.len() == 1 977 it.next() 978 assert it.len() == 0 979 ``` 980 """ 981 return len(self)
Returns the remaining number of items in the iterator.
This doesn't exhaust the iterator.
Example
it = once(0)
assert it.len() == 1
assert it.len() == 1
it.next()
assert it.len() == 0
983 @typing.final 984 def is_empty(self) -> bool: 985 """Return `True` if this iterator has no items left to yield. 986 987 Example 988 ------- 989 ```py 990 iterator = once(1) 991 assert not iterator.is_empty() 992 assert once.next() == Some(1) 993 assert iterator.is_empty() 994 ``` 995 """ 996 return len(self) == 0
Return True if this iterator has no items left to yield.
Example
iterator = once(1)
assert not iterator.is_empty()
assert once.next() == Some(1)
assert iterator.is_empty()
1580@rustc_diagnostic_item("into_iter") 1581def into_iter( 1582 iterable: collections.Sequence[Item] | collections.Iterable[Item], 1583) -> Iter[Item] | TrustedIter[Item] | TrustedIter[int]: 1584 """Convert any iterable into `Iterator[Item]`. 1585 1586 if the size of the iterable is known, it will return `TrustedIter`, 1587 otherwise it will return `Iter`. 1588 1589 Example 1590 ------- 1591 ```py 1592 sequence = [1,2,3] 1593 for item in sain.into_iter(sequence).reversed(): 1594 print(item) 1595 # 3 1596 # 2 1597 # 1 1598 ``` 1599 """ 1600 if isinstance(iterable, collections.Sequence): 1601 return TrustedIter(iterable) 1602 return Iter(iterable)
Convert any iterable into Iterator[Item].
if the size of the iterable is known, it will return TrustedIter,
otherwise it will return Iter.
Example
sequence = [1,2,3]
for item in sain.into_iter(sequence).reversed():
print(item)
# 3
# 2
# 1
Implementations
This function implements .into_iter">into_iter in Rust.
1513@rustc_diagnostic_item("empty") 1514def empty() -> Empty[Item]: # pyright: ignore 1515 """Create an iterator that yields nothing. 1516 1517 Example 1518 ------- 1519 ```py 1520 nope: Iterator[int] = sain.iter.empty() 1521 assert nope.next().is_none() 1522 ``` 1523 """ 1524 return Empty()
Create an iterator that yields nothing.
Example
nope: Iterator[int] = sain.iter.empty()
assert nope.next().is_none()
Implementations
This function implements empty in Rust.
1552@rustc_diagnostic_item("once") 1553def once(item: Item) -> Once[Item]: 1554 """Returns an iterator that yields exactly a single item. 1555 1556 Example 1557 ------- 1558 ```py 1559 iterator = sain.iter.once(1) 1560 assert iterator.next() == Some(1) 1561 assert iterator.next() == Some(None) 1562 ``` 1563 """ 1564 return Once(item)
Returns an iterator that yields exactly a single item.
Example
iterator = sain.iter.once(1)
assert iterator.next() == Some(1)
assert iterator.next() == Some(None)
Implementations
This function implements once in Rust.
1527@rustc_diagnostic_item("repeat") 1528def repeat(element: Item, count: int) -> Repeat[Item]: 1529 """Returns an iterator that yields the exact same `element` number of `count` times. 1530 1531 The yielded elements is a copy of `element`, but the last element is guaranteed to be the same as the 1532 original `element`. 1533 1534 Example 1535 ------- 1536 ```py 1537 nums = [1, 2, 3] 1538 it = sain.iter.repeat(nums, 5) 1539 for i in range(4): 1540 cloned = it.next().unwrap() 1541 assert cloned == [1, 2, 3] 1542 1543 # But the last item is the origin one... 1544 last = it.next().unwrap() 1545 last.append(4) 1546 assert nums == [1, 2, 3, 4] 1547 ``` 1548 """ 1549 return Repeat(element, count)
Returns an iterator that yields the exact same element number of count times.
The yielded elements is a copy of element, but the last element is guaranteed to be the same as the
original element.
Example
nums = [1, 2, 3]
it = sain.iter.repeat(nums, 5)
for i in range(4):
cloned = it.next().unwrap()
assert cloned == [1, 2, 3]
# But the last item is the origin one...
last = it.next().unwrap()
last.append(4)
assert nums == [1, 2, 3, 4]
Implementations
This function implements repeat in Rust.