Поведение Python "доходность" - PullRequest
10 голосов
/ 09 сентября 2011

Я читаю о ключевом слове yield в python и пытаюсь понять, как запустить этот пример:

def countfrom(n):
    while True:
        print "before yield"
        yield n
        n += 1
        print "after yield"

for i in countfrom(10):
    print "enter for loop"
    if i <= 20:
        print i
    else:
        break

Вывод:

before yield
enter for loop
10
after yield
before yield
enter for loop
11
after yield
before yield
enter for loop
12
after yield
before yield
enter for loop
13
after yield
before yield
enter for loop
14
after yield
before yield
enter for loop
15
after yield
before yield
enter for loop
16
after yield
before yield
enter for loop
17
after yield
before yield
enter for loop
18
after yield
before yield
enter for loop
19
after yield
before yield
enter for loop
20
after yield
before yield
enter for loop

Это похоже наyield вернет указанное значение и продолжит выполнение функции до конца (возможно, в параллельном потоке).Правильно ли мое понимание?

Если бы вы могли ответить на это, не упомянув "генераторы", я был бы благодарен, потому что я пытаюсь понять по одному.

Ответы [ 6 ]

22 голосов
/ 09 сентября 2011

Вы можете думать об этом как о функции, которая yield s просто "делает паузу", когда она сталкивается с yield. При следующем вызове он возобновится после yield , сохранив состояние, в котором он находился, когда покинул .

7 голосов
/ 09 сентября 2011

Нет, есть только одна нить.

Каждая итерация цикла for запускает вашу функцию countFrom, пока она не выдаст что-либо или не вернет. После yield снова запускается тело цикла for, а затем, когда начинается новая итерация, функция countFrom выбирает именно то место, где она остановилась, и запускается снова, пока не вернется (или не вернется).

Эта измененная версия вашего примера поможет вам понять, какой путь выполняется.

def countfrom(n):
    while n <= 12:
        print "before yield, n = ", n
        yield n
        n += 1
        print "after yield, n = ", n

for i in countfrom(10):
    print "enter for loop, i = ", i
    print i
    print "end of for loop iteration, i = ", i

выход

before yield, n =  10
enter for loop, i =  10
10
end of for loop iteration, i =  10
after yield, n =  11
before yield, n =  11
enter for loop, i =  11
11
end of for loop iteration, i =  11
after yield, n =  12
before yield, n =  12
enter for loop, i =  12
12
end of for loop iteration, i =  12
after yield, n =  13
6 голосов
/ 09 сентября 2011

.. вы не можете объяснить значение оператора yield без упоминания генераторов; это все равно что пытаться объяснить, что такое камень, не говоря уже о скале. То есть: оператор yield является ответственным за преобразование нормальной функции в генератор.

Пока вы найдете это хорошо задокументированным здесь: http://docs.python.org/reference/simple_stmts.html#the-yield-statement

.. краткое объяснение этого:

  • Когда вызывается функция, использующая оператор yield, она возвращает «итератор генератора», имеющий метод .next() (стандарт для итерируемых объектов)
  • Каждый раз, когда вызывается метод .next() генератора (например, путем итерации объекта с циклом for), функция вызывается до тех пор, пока не встретится первый выход. Затем выполнение функции приостанавливается и в качестве возвращаемого значения метода .next() передается значение.
  • В следующий раз, когда вызывается .next(), выполнение функции возобновляется до следующего yield и т. Д., Пока функция не возвращает что-либо.

Некоторые преимущества в этом:

  • меньше использования памяти, поскольку память выделяется только для текущего полученного значения, а не для всего списка возвращаемых значений (как это было бы при возврате списка значений)
  • Результаты «реального времени» возвращаются, поскольку они могут быть переданы вызывающей стороне, не дожидаясь окончания генерации (я использовал это для возврата вывода из запущенного процесса)
1 голос
/ 09 сентября 2011

Функция countfrom не выполняется в параллельном потоке.Здесь происходит то, что всякий раз, когда for -структура запрашивает следующее значение, функция будет выполняться до тех пор, пока не будет достигнут оператор yield.Когда требуется следующее значение после этого, функция возобновляет выполнение с того места, где она остановилась.

И хотя вы просили не упоминать «генераторы», они настолько тесно связаны с yield, что недействительно имеет смысл говорить о отдельно.То, что на самом деле возвращает countfrom функция, является «объектом-генератором».Он возвращает этот объект сразу после его вызова, поэтому тело функции вообще не выполняется, пока что-то (например, for -loop) не запросит значения у генератора, используя его метод .next().

0 голосов
/ 09 сентября 2011

Python работает до тех пор, пока не достигнет yield, а затем останавливает и останавливает выполнение.Это не продолжает бежать.При следующем вызове он нажимает «после» на countfrom

Легко сказать, что без ссылки на генераторы, но факт в том, что yield и генератор неразрывно связаны.Чтобы действительно понять это, вы должны рассматривать их как одну и ту же тему.

Легко показать себе, что сказанное мною (и другими) является правдой, если поработать с генератором из вашего примера более ручным способом.

Функция, которая yield s вместо return ing действительно возвращает генератор.Затем вы можете использовать этот генератор, вызвав next.Вы сбиты с толку, потому что ваш цикл позаботится обо всем, что в фоновом режиме для вас.

Вот оно с раскрытыми внутренностями:

0 голосов
/ 09 сентября 2011

оператор yield хранит полученное вами значение, пока эта функция не будет вызвана снова. так что если вы вызовете эту функцию (с итератором), она запустит функцию в другой раз и выдаст вам значение. дело в том, что он знает, где остановился в прошлый раз

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