Напишем функцию, которая возвращает последовательность трёх элементов: чисел 1,2,3. Простейшее решение -- return [1,2,3]. Но мы напишем функцию, которая возвращает результат с помощью генераторов, а также имеет инициализатор в начале (строка 2). И пройдёмся по её результату для вывода на stdout (строки 19-22).
Забавы ради, иницилизатор сделаем "глючным", и он нам будет выкидывать исключение (строка 2). Но мы умные, мы исключения от этой функции ожидаем, и будем их ловить (строка 14), и в таком случае результат подразумевать пустым (строка 16).
Просто, да? Вот код:
- def generator():
- raise Exception("dieplz")
- yield 1
- yield 2
- yield 3
- try:
- print('==============================')
- try:
- print("Calling function")
- items = generator()
- print("Call succeded")
- except Exception, e:
- print("Call failed with exception: %s" % e)
- items = []
- print('==============================')
- print("Iterating over items")
- for item in items:
- print("Item: %s" % item)
- print("Iteration finished")
- except Exception, e:
- print("Failed with unhandled exception: %s" % e)
- finally:
- print("Done")
- print('==============================')
Но не тут-то было! Эта функция НЕ выполняется в момент вызова функции (на строке 12). Вместо этого на невидимой прослойке интерпретатора возвращается некий объект-генератор, который попадает в переменную items. И только попытка итерации по нему (строка 20) уже реально вызывает функцию.
И исключение, которое в функции должно происходить даже до первой "заморозки выполнения", случается не в том месте, где его ждут (на строке 20 вместо ожидаемой 12). И мы ловим unhandled exception вместо итерации по пустому анти-ошибочному списку.
>python generators.py
==============================
Calling function
Call succeded
==============================
Iterating over items
Failed with unhandled exception: dieplz
Done
==============================
==============================
Calling function
Call succeded
==============================
Iterating over items
Failed with unhandled exception: dieplz
Done
==============================
Решение проблемы простое - всегда пригонять результаты генератором в известный тип: items = list(generator()). Но это решение неудобное. А если эта функция - callback, который задаётся кем-то снаружи? А если им захочется передать не колбек, а просто экземпляр итерабельного объекта? А если этот объект ещё изменит своё состояние до цикла for, и итерация действительно должна быть только в том месте?
Не без изъянов, в общем, язычок-то.
December 17 2009, 03:07:39 UTC 3 years ago
Так что твоя проблема - в недопонимании того, что вызывая generator() ты вызваешь не тело функции, а всего лишь создаёшь генератор, т.к. функция, определённая с yield превращается в функцию-генератор )
Вот там курили:
http://www.python.org/dev/peps/pep-0
http://www.python.org/dev/peps/pep-0
December 17 2009, 04:53:10 UTC 3 years ago
Deleted comment
January 4 2010, 20:05:42 UTC 3 years ago
Один раз учил и практиковал когда админил/со-админил у провайдера и для себя. Всякие скрипты, парсеры, и пр.
Второй раз учил когда думал туда переползти с пхп; но, увидев тамошний "ООП", испугался и вытер из памяти чтоб не повредить своё мировоззрение %-)
Deleted comment
January 4 2010, 22:25:20 UTC 3 years ago
Замыкания - ок, есть в питоне красивые, есть в JS. Если JS не пошёл в массы поскольку сфера применения ограничена по факту (хотя что мешает расширить), то почему питон не идёт - непонятно. Видимо, замыкания - удел гуи и прочего интерактивного, как альтернативное решение для передачи состояния. Не вебовское точно, не скриптовое.
Декларативное ждём-с. Пока что это вещь в себе, судить не о чем. Хотя, кстати... А чё ждать? У меня с 2002 года в голове концепты и наброски как программировать на естественном языке и как это парсить и интерпретировать. Надо брать и разрабатывать. Ай, лентяй :-)
Deleted comment
January 4 2010, 23:13:28 UTC 3 years ago
December 18 2009, 17:59:25 UTC 3 years ago
xxx: можешь сказать в чем главная ошибка
yyy: он изначально рассматривает генератор как функцию и ожидает что она сразу начнет выполняться
xxx: хм, а как надо?
yyy: вызов генератора лучше рассматривать как создание объекта
xxx: понятно, спасиб
December 18 2009, 18:42:15 UTC 3 years ago
try: items = callback()
except: items = []
for item in items:
print(item)
def fn1(): return [1,2,3]
def fn2(): yield 10
doit(fn1)
doit(fn2)
Простой, казалось бы, синтаксис у doit. Вменяемый. А вот юз-кейс подкачал. Потому что "фокус".
December 18 2009, 18:46:41 UTC 3 years ago