Неожиданное поведение декоратора, оператор if выполняется после if 1 == 2: - PullRequest
0 голосов
/ 02 января 2019

Несколько часов назад я отправил связанный вопрос и получил ответ, почему мне нужно добавить yield в мой декоратор для правильной работы. Я недавно вспомнил, что я пропустил это по причине - странное поведение, которое я не могу объяснить.

Я заранее прошу прощения, если я слепой, но я часами смотрел на этот код и играл с ним, и вот что я получаю:

def decor(func):
    def wrapper(*args, **kwargs):
        if 1==2:
            print ("Generator")
            for item in func(*args, **kwargs):
                print(item)
                #yield(item)
        else:
            print ("Not generator")
            res = func(*args, **kwargs)
            print(res)
            return res
    return wrapper

@decor            
def f():
    return "a"


f()

"""
Output:
    Not generator
    a

"""

А если я удалю комментарий до yield, то вообще ничего не выйдет.

Почему это? И как это возможно, что все, что я изменяю в операторе if 1==2:, влияет на сценарий?

1 Ответ

0 голосов
/ 02 января 2019

Если функция содержит yield в любом месте в теле, это функция генератора.Неважно, выполняется yield или нет.Тот факт, что 1 == 2 имеет значение false, не имеет к этому никакого отношения.

Рассмотрим следующую функцию:

def addone(numbers):
    for number in numbers:
        yield number + 1

Что происходит при вызове addone([])?yield никогда не выполняется, и все же addone по-прежнему возвращает генератор.Почему это должно быть иначе:

def addone(numbers):
    if numbers:
        for number in numbers:
            yield number + 1

Таким образом, становится ясно, что фактическое выполнение yield не имеет значения.Единственный релевантный факт - существует ли yield в теле функции.

Как исправить функцию

Исправление относительно простое, все, что вам нужно сделать, это потянуть частьс yield в отдельную функцию:

import types

def wrapper(*args, **kwargs):
    result = func(*args, **kwargs)
    if isinstance(result, types.GeneratorType):
        print("Is a generator")
        return wrap_generator(result)
    print("Not a generator")
    return result

def wrap_generator(gen):
    for item in gen:
        print(item)
        yield item

Как избежать в будущем

В общем, проблема здесь в том, что функция является либо генератором (и использует yield) или является нормальной функцией (и использует return).Это немного сбивает с толку, когда вы используете yield и return в одной и той же функции!

Для Python получается, что если вы используете и 1035 *, и return в одной и той же функции,функция является функцией генератора.Это может несколько сбивать с толку, поэтому из соображений стиля я бы обычно избегал использования return и yield в одной и той же функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...