1 Востаннє редагувалося ping (07.04.2018 21:55:46)

Тема: про генератори і декоратори

я написав красивий клас Веселка, який вміє друкувати кольори веселки безконечно по циклу
ось:

class Rainbow:
    colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet',]

    def __init__(self, circle=True):
        self.circle = circle
    
   
    def _get_colors(self):
        i = 0
        while self.circle:
            yield self.colors[i]
            i += 1
            i %= len(self.colors)
            
    
    def __iter__(self):
        if self.circle:
            iterator = iter(self._get_colors())
        else:
            iterator = iter(self.colors)
        return iterator

   
for color in Rainbow(circle=False):
    print(color)

input('Press "Enter" to continue')        
    
for color in Rainbow():
    print(color)

але , виникла потреба виводити кольори з затримкою 2 секунди
написав декоратор:

import time


def sleep_it(f):
    def wrapper(*args, **kwargs):
        time.sleep(2)
        f(*args, **kwargs)
    return wrapper

обгорнув _get_colors(self) і отримав дулю(TypeError: 'NoneType' object is not iterable).
чому я не можу застосувати декоратор до генератора?

2 Востаннє редагувалося P.Y. (07.04.2018 18:27:20)

Re: про генератори і декоратори

Елементарно: в передостанньому рядку забули написати return, щоб повернути результат виклику f.

Оскільки в нас тут yield, самим return'ом теж не обійтись (так ми просто зробимо затримку перед пешим елементом, решта підуть без затримки). Потрібно в декораторі реалізувати цикл з затримками та yield'ами...

Подякували: ping1

3

Re: про генератори і декоратори

Якось так:

def sleep_it(f):
    def wrapper(*args, **kwargs):
        for i in f(*args, **kwargs):
            time.sleep(2)
            yield i
    return wrapper
Подякували: ping1

4 Востаннє редагувалося ping (07.04.2018 22:26:29)

Re: про генератори і декоратори

P.Y. написав:

Якось так:

def sleep_it(f):
    def wrapper(*args, **kwargs):
        for i in f(*args, **kwargs):
            time.sleep(2)
            yield i
    return wrapper

дякую.
працює. (ще б чітко зрозуміти чому ...)
Але!
Виникла потреба створити власний ітератор для "невічного" виводу кольорів (коли circle=False)
вирішили, що ним буде наш клас, тому в ньому
визначили метод __next__() і з якихось причин він має бути НЕ генератором
все працює

    import time
    
    def sleep_it(f):
        def wrapper(*args, **kwargs):
            for i in f(*args, **kwargs):
                time.sleep(2)
                yield i
        return wrapper
    
    def sleep_it_old(f):
        def wrapper(*args, **kwargs):
            time.sleep(2)
            return f(*args, **kwargs)
        return wrapper
    
    class Rainbow:
        colors = ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet',]
        length_loop = len(colors)
        
        def __init__(self, circle=True):
            self.circle = circle
            self.count = -1
        
        @sleep_it
        def _get_colors(self):
            i = 0
            while self.circle:
                yield self.colors[i]
                i += 1
                i %= len(self.colors)
                
        
        def __iter__(self):
            if self.circle:
                iterator = iter(self._get_colors())
            else:
                iterator = self
            return iterator
        
        # @sleep_it
        @sleep_it_old
        def __next__(self):                       
            if self.count < self.length_loop - 1:
                self.count += 1
                return self.colors[self.count]
            else:
                raise StopIteration
       
    for color in Rainbow(circle=False):
        print(color)
     
    input('Press "Enter" to continue')        
        
    for color in Rainbow():
        print(color)

Але, наш декоратор @sleep_it тепер не годиться для __next__()  - повертає генератор, замість значення
зате попередня версія (звісно, додав return ) ) - працює
писати різні декоратори для функцій з yield та return?

5

Re: про генератори і декоратори

писати різні декоратори для функцій з yield та return?

Мабуть. __next__ викликається при отриманні кожного елементу (а отже, затримка в декораторі потрібна одна), тоді як функція з yield'ами викликається один раз і переривається при передачі кожного елементу — відповідно, затримку треба робити стільки разів, скільки елементів у згенерованій послідовності.

Подякували: ping1

6

Re: про генератори і декоратори

P.Y. написав:

писати різні декоратори для функцій з yield та return?

Мабуть. __next__ викликається при отриманні кожного елементу (а отже, затримка в декораторі потрібна одна), тоді як функція з yield'ами викликається один раз і переривається при передачі кожного елементу — відповідно, затримку треба робити стільки разів, скільки елементів у згенерованій послідовності.

*THUMBSUP*