Подтвердить что ты не робот

Почему генераторы не могут мариноваться?

Python pickle (я говорю о стандартном Python 2.5/2.6/2.7 здесь) не может рассортировать блокировки, файловые объекты и т.д.

Он также не может рассортировать генераторы и лямбда-выражения (или любой другой анонимный код), потому что рассол действительно хранит только ссылки на имена.

В случае блокировок и функций, зависящих от ОС, причина, по которой вы не можете их рассортировать, очевидна и имеет смысл.

Но почему вы не можете распиливать генераторы?


Примечание: просто для ясности - меня интересует фундаментальная причина (или предположения и выбор, которые вошли в это дизайнерское решение), почему, а не в "потому что это дает вам ошибку Pickle".

Я понимаю вопрос немного широким, так что вот эмпирическое правило о том, ответил ли вы на него: "Если бы эти предположения были подняты или тип разрешенного генератора был каким-то образом ограниченным, могли бы снова генерировать генераторы?"

4b9b3361

Ответ 1

Существует много информации об этом. Для "официального слова" по этому вопросу прочитайте (закрытый) вопрос об ошибке Python.

Основная аргументация одного из людей, принявших решение, подробно описана в в этом блоге:

Поскольку генератор - это сугубо функция, нам нужно сохранить его байт-код, который не гарантирует обратную совместимость между версиями Pythons и его фреймом, который содержит состояние генератора, такое как локальные переменные, замыкания и указатель команд. И это последнее довольно громоздко, потому что в основном это требует, чтобы весь интерпретатор был сорван. Таким образом, любая поддержка генераторов травления потребует большого количества изменений в ядре CPythons.

Теперь, если объект, не поддерживаемый pickle (например, дескриптор файла, сокет, соединение с базой данных и т.д.), встречается в локальных переменных генератора, тогда этот генератор не может быть маринован автоматически, независимо от поддержки рассола для генераторы, которые мы можем реализовать. Таким образом, в этом случае вам все равно необходимо предоставить настраиваемые методы __getstate__ и __setstate__. Эта проблема делает любую поддержку травления для генераторов весьма ограниченной.

И упоминаются две предложенные обходные пути:

В любом случае, если вам нужна такая функция, загляните в Stackless Python, который сделает все выше. А так как интерпретатор Stacklesss является граблированным, вы также получаете бесплатную миграцию процесса. Это означает, что вы можете прервать тасклет (имя для зеленых потоков Stacklesss), рассолить его, отправить рассол на другую машину, разложить ее, возобновить тасклет, а voilà вы просто перенесли процесс. Это потрясающая функция!

Но по моему скромному мнению, наилучшим решением этой проблемы переписать генераторы как простые итераторы (т.е. один с методом __next__). Итераторы легки и эффективны в пространстве для рассола, потому что их состояние является явным. Однако вам все равно придется обрабатывать объекты, представляющие какое-либо внешнее состояние; вы не можете обойти это.

Ответ 2

Фактически вы можете, в зависимости от реализации. PyPy и Stackless Python позволяют это ( в какой-то степени):

Python 2.7.1 (dcae7aed462b, Aug 17 2011, 09:46:15)
[PyPy 1.6.0 with GCC 4.0.1] on darwin
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``Not your usual analyses.''
>>>> import pickle
>>>> gen = (x for x in range(100))
>>>> next(gen)
0
>>>> pickled = pickle.dumps(gen)
>>>> next(pickle.loads(pickled))
1

В CPython также возможно создать объект итератора для имитации генератора с возможностью выбора.