Custom Generators#

from collections.abc import Generator

class IntAndCharSequence(Generator):
    
    def __init__(self) -> None:
        
        self._item = {
            "integer": 0,
            "character": "a"
        }
        
    def send(self, value):
        
        item = self._item.copy()
        
        if item["integer"] == 10:
            raise StopIteration
        
        self._item["integer"] += 1
        self._item["character"] = chr(ord(self._item["character"]) + 1)
        
        # import time
        # time.sleep(0.1)
        
        return item
        
    def throw(self, typ, val=None, tb=None):
        """Raise an exception in the generator.
        Return next yielded value or raise StopIteration.
        
        Notes
        -----
            This implementation is copied from https://github.com/python/cpython/blob/d5d3249e8a37936d32266fa06ac20017307a1f70/Lib/_collections_abc.py#L309.
        """
        
        if val is None:
            if tb is None:
                raise typ
            val = typ()
            
        if tb is not None:
            val = val.with_traceback(tb)
            
        raise val
    
for i, item in enumerate(IntAndCharSequence()):
    print(item)
    if i == 20:
        break
{'integer': 0, 'character': 'a'}
{'integer': 1, 'character': 'b'}
{'integer': 2, 'character': 'c'}
{'integer': 3, 'character': 'd'}
{'integer': 4, 'character': 'e'}
{'integer': 5, 'character': 'f'}
{'integer': 6, 'character': 'g'}
{'integer': 7, 'character': 'h'}
{'integer': 8, 'character': 'i'}
{'integer': 9, 'character': 'j'}
import operator
import functools
operator.itemgetter("integer")
operator.itemgetter('integer')
for i, item in enumerate(map(lambda item: item.get("integer"), IntAndCharSequence())):
    print(item)
    if i == 9:
        break
0
1
2
3
4
5
6
7
8
9
for i, item in enumerate(map(operator.itemgetter("integer"), IntAndCharSequence())):
    print(item)
    if i == 9:
        break
0
1
2
3
4
5
6
7
8
9
g = map(lambda item: item.get("integer"), IntAndCharSequence())
isinstance(g, Generator)
False
from operator import getitem
from functools import partial

book = {
    "title": "A Python Notebook",
    "chapters": [
        {
            "title": "Foundamentals",
            "sections": [
                "Custom Generators"
            ]
        }
    ]
}

getitem(getitem(book, "chapters"), 0)
{'title': 'Foundamentals', 'sections': ['Custom Generators']}
partial(getitem, "chapters")
functools.partial(<built-in function getitem>, 'chapters')
from typing import Self
from abc import ABC, abstractmethod
from typing import Any

class PipelineComponent(ABC):
    
    def __call__(self, input: Any) -> Any:
        
        output = self.forward(input)
        
        return output
        
    def __or__(self, other: Self) -> Self:
        
        this = self
        
        class _CombinedComponent(PipelineComponent):
            
            def forward(self, input: Any) -> Any:
                return other.forward(this.forward(input))
        
        component = _CombinedComponent()
        
        return component
    
    @abstractmethod
    def forward(self, input: Any) -> Any:
        pass

class GetItem(PipelineComponent):
    
    def __init__(self, key: Any) -> None:
        
        super().__init__()
        
        self._key = key
    
    def forward(self, input: Any) -> Any:
        
        return getitem(input, self._key)
    
(GetItem("chapters") | GetItem(0) | GetItem("sections") | GetItem(0))(book)
'Custom Generators'
(GetItem("chapters") | GetItem(0) | GetItem("sections"))
<__main__.PipelineComponent.__or__.<locals>._CombinedComponent at 0x104674a90>

Asynchronous Generators#

https://gist.github.com/jspahrsummers/32a8096667cf9f17d5e8fddeb081b202