pyiter.transform

  1from abc import ABC, abstractmethod
  2from typing import Any, Generic, Iterable, Iterator, List, Optional, TypeVar, Generator
  3
  4
  5T = TypeVar("T")
  6
  7U = TypeVar("U")
  8
  9O = TypeVar("O")
 10
 11K = TypeVar("K")
 12
 13
 14class Transform(ABC, Generic[T, U], Iterable[U]):
 15    """A transform that applies a function to an iterable."""
 16
 17    iter: Iterable[T]
 18    cache: Optional[List[U]]
 19
 20    def __init__(self, iter: Iterable[T]):
 21        from .sequence import Sequence
 22
 23        self.iter = iter.__transform__ if isinstance(iter, Sequence) else iter
 24        self.cache = None
 25
 26    def __iter__(self) -> Iterator[U]:
 27        if self.cache:
 28            yield from self.cache
 29        else:
 30            cache: List[U] = []
 31            for x in self.__do_iter__():
 32                cache.append(x)
 33                yield x
 34            self.cache = cache
 35
 36    def __len__(self) -> int:
 37        # if not isinstance(self.iter, Transform):
 38        #     # not Sequence, just a wrapper of List, Tuple.etc.
 39        #     # we can get lenght of it directly.
 40        #     if hasattr(self.iter, '__len__'):
 41        #         return len(self.iter) # type: ignore
 42        #     elif hasattr(self.iter, '__length_hint__'):
 43        #         return self._iter.__length_hint__() # type: ignore
 44        # we need iterate all to get length
 45        cache = self.cache
 46        if cache is None:
 47            for _ in self:
 48                pass
 49            cache = self.cache
 50        if cache is not None:
 51            return len(cache)
 52        return 0
 53
 54    def __repr__(self) -> str:
 55        return self.__class__.__name__
 56
 57    @abstractmethod
 58    def __do_iter__(self) -> Iterator[U]:
 59        raise NotImplementedError
 60
 61    def transforms(self) -> "Iterable[Transform[Any, Any]]":
 62        inner = self.iter
 63        if isinstance(inner, Transform):
 64            yield from inner.transforms()
 65        yield self
 66
 67
 68class NonTransform(Transform[T, T]):
 69    """
 70    A [Transform] that does not transform the values.
 71    """
 72
 73    def __init__(self, iter: Iterable[T]) -> None:
 74        super().__init__(iter)
 75        self.cache = iter if isinstance(iter, list) else [*iter]
 76
 77    def __repr__(self) -> str:
 78        return f"{self.__class__.__name__}[{self.iter.__class__.__name__}]"
 79
 80    def __do_iter__(self) -> Iterator[T]:
 81        yield from self.iter
 82
 83    def __len__(self) -> int:
 84        if not isinstance(self.iter, Transform):
 85            # not Sequence, just a wrapper of List, Tuple.etc.
 86            # we can get lenght of it directly.
 87            if hasattr(self.iter, "__len__"):
 88                return len(self.iter)  # type: ignore
 89            elif hasattr(self.iter, "__length_hint__"):
 90                return self.iter.__length_hint__()  # type: ignore
 91        return super().__len__()
 92
 93
 94class InfinityTransform(Transform[T, T]):
 95    """Transform that iterates over an infinite sequence."""
 96
 97    def __init__(self, iter: Iterable[T]) -> None:
 98        super().__init__(iter)
 99
100    def __repr__(self) -> str:
101        return f"{self.__class__.__name__}[{self.iter.__class__.__name__}]"
102
103    def __do_iter__(self) -> Iterator[T]:
104        yield from self.iter
105
106    def __len__(self) -> int:
107        if self.cache is not None:
108            return len(self.cache)
109        raise OverflowError("Cannot determine the length of an infinite sequence.")
110
111
112def new_transform(iter: Iterable[T]) -> Transform[Any, T]:
113    from .sequence import Sequence
114
115    if isinstance(iter, Sequence):
116        return iter.__transform__
117    if isinstance(iter, Transform):
118        return iter  # type: ignore
119
120    if isinstance(iter, Generator):
121        return InfinityTransform(iter)  # type: ignore
122
123    return NonTransform(iter)
class Transform(abc.ABC, typing.Generic[~T, ~U], typing.Iterable[~U]):
15class Transform(ABC, Generic[T, U], Iterable[U]):
16    """A transform that applies a function to an iterable."""
17
18    iter: Iterable[T]
19    cache: Optional[List[U]]
20
21    def __init__(self, iter: Iterable[T]):
22        from .sequence import Sequence
23
24        self.iter = iter.__transform__ if isinstance(iter, Sequence) else iter
25        self.cache = None
26
27    def __iter__(self) -> Iterator[U]:
28        if self.cache:
29            yield from self.cache
30        else:
31            cache: List[U] = []
32            for x in self.__do_iter__():
33                cache.append(x)
34                yield x
35            self.cache = cache
36
37    def __len__(self) -> int:
38        # if not isinstance(self.iter, Transform):
39        #     # not Sequence, just a wrapper of List, Tuple.etc.
40        #     # we can get lenght of it directly.
41        #     if hasattr(self.iter, '__len__'):
42        #         return len(self.iter) # type: ignore
43        #     elif hasattr(self.iter, '__length_hint__'):
44        #         return self._iter.__length_hint__() # type: ignore
45        # we need iterate all to get length
46        cache = self.cache
47        if cache is None:
48            for _ in self:
49                pass
50            cache = self.cache
51        if cache is not None:
52            return len(cache)
53        return 0
54
55    def __repr__(self) -> str:
56        return self.__class__.__name__
57
58    @abstractmethod
59    def __do_iter__(self) -> Iterator[U]:
60        raise NotImplementedError
61
62    def transforms(self) -> "Iterable[Transform[Any, Any]]":
63        inner = self.iter
64        if isinstance(inner, Transform):
65            yield from inner.transforms()
66        yield self

A transform that applies a function to an iterable.

iter: Iterable[~T]
cache: Optional[List[~U]]
def transforms(self) -> Iterable[pyiter.transform.Transform[Any, Any]]:
62    def transforms(self) -> "Iterable[Transform[Any, Any]]":
63        inner = self.iter
64        if isinstance(inner, Transform):
65            yield from inner.transforms()
66        yield self
class NonTransform(pyiter.transform.Transform[~T, ~T]):
69class NonTransform(Transform[T, T]):
70    """
71    A [Transform] that does not transform the values.
72    """
73
74    def __init__(self, iter: Iterable[T]) -> None:
75        super().__init__(iter)
76        self.cache = iter if isinstance(iter, list) else [*iter]
77
78    def __repr__(self) -> str:
79        return f"{self.__class__.__name__}[{self.iter.__class__.__name__}]"
80
81    def __do_iter__(self) -> Iterator[T]:
82        yield from self.iter
83
84    def __len__(self) -> int:
85        if not isinstance(self.iter, Transform):
86            # not Sequence, just a wrapper of List, Tuple.etc.
87            # we can get lenght of it directly.
88            if hasattr(self.iter, "__len__"):
89                return len(self.iter)  # type: ignore
90            elif hasattr(self.iter, "__length_hint__"):
91                return self.iter.__length_hint__()  # type: ignore
92        return super().__len__()

A [Transform] that does not transform the values.

class InfinityTransform(pyiter.transform.Transform[~T, ~T]):
 95class InfinityTransform(Transform[T, T]):
 96    """Transform that iterates over an infinite sequence."""
 97
 98    def __init__(self, iter: Iterable[T]) -> None:
 99        super().__init__(iter)
100
101    def __repr__(self) -> str:
102        return f"{self.__class__.__name__}[{self.iter.__class__.__name__}]"
103
104    def __do_iter__(self) -> Iterator[T]:
105        yield from self.iter
106
107    def __len__(self) -> int:
108        if self.cache is not None:
109            return len(self.cache)
110        raise OverflowError("Cannot determine the length of an infinite sequence.")

Transform that iterates over an infinite sequence.

def new_transform(iter: Iterable[~T]) -> pyiter.transform.Transform[typing.Any, ~T]:
113def new_transform(iter: Iterable[T]) -> Transform[Any, T]:
114    from .sequence import Sequence
115
116    if isinstance(iter, Sequence):
117        return iter.__transform__
118    if isinstance(iter, Transform):
119        return iter  # type: ignore
120
121    if isinstance(iter, Generator):
122        return InfinityTransform(iter)  # type: ignore
123
124    return NonTransform(iter)