Skip to content

Latest commit

 

History

History
152 lines (120 loc) · 3.95 KB

File metadata and controls

152 lines (120 loc) · 3.95 KB

Examples

Working with generator functions

Generator is a special type of function in Python that behaves as an iterator. It is both easier to read and more efficient than using list.append when building an iterable.

However in some cases a generator by itself may not fulfill what is needed. In this case it can be combined with decorators to achieve more complex functionality.

  • Example 1

    (Script available)

    def execute(type=tuple):
        """
        Execute a generator and store results in a storage (default is tuple)
        """
    
        def __decor(gen):
            @functools.wraps(gen)
            def __new_func(*args, **kwargs):
                return type(gen(*args, **kwargs))
    
            return __new_func
    
        return __decor
    
    @execute()
    def read_all(path):
        for line in read_lines(path):
            if line.startswith('#'):
                continue
            yield line
  • Example 2

    (Script available)

    def partial(
            empty_error: Union[bool, Type[Exception]] = False
    ):
        """
        Decorates a generator so that when called,
        it's partially executed until the first `yield` statement.
    
        If the result is empty (i.e. a `yield` statement is never reached),
        0. The generator will be fully executed; and
        1. If `empty_error` is False (default), an empty tuple will be returned; otherwise
        2. If `empty_error` is True, a StopIteration will be raised; otherwise
        3. `empty_error()` will be raised
        """
    
        def __decor(gen):
            @functools.wraps(gen)
            def __new_func(*args, **kwargs):
                iterator = gen(*args, **kwargs)
                try:
                    first = next(iterator)
                except StopIteration:
                    if not empty_error:
                        return ()
                    elif isinstance(empty_error, bool):
                        # empty_error = True
                        raise
                    else:
                        raise empty_error() from None
    
                def __new_generator():
                    yield first
                    for item in iterator:
                        yield item
    
                return __new_generator()
    
            return __new_func
    
        return __decor
    
    class Float(Exception):
        pass
    
    @partial()
    def read_int(path):
        for line in read_lines(path):
            if line.startswith('#'):
                if 'float' in line:
                    raise Float
                continue
            try:
                yield int(line)
            except ValueError:
                continue
    
    def read_float(path):
        for line in read_lines(path):
            if line.startswith('#'):
                continue
            try:
                yield float(line)
            except ValueError:
                continue
    
    def read(path):
        try:
            return read_int(path)
        except Float:
            return read_float(path)
  • Example 3

    (Script available)

    def report(log=print, name=None, interval=1000):
        """
        Report progress of a generator
        """
    
        def __decor(gen):
            _name = name or gen.__name__
    
            @functools.wraps(gen)
            def __new_func(*args, **kwargs):
                count = 0
                for item in gen(*args, **kwargs):
                    yield item
                    count += 1
                    if count % interval == 0:
                        log("{}: yielded {} entries".format(_name, count))
                log("{}: finished with {} entries".format(_name, count))
    
            return __new_func
    
        return __decor
    
    @report()
    def read(path):
        for line in read_lines(path):
            if line.startswith('#'):
                continue
            yield line

Prev / Up / Next