Почему нельзя мариновать генераторы? - PullRequest
34 голосов
/ 24 августа 2011

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

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

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

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


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

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

Ответы [ 2 ]

47 голосов
/ 24 августа 2011

Существует много информации об этом. Чтобы прочитать «официальное слово» об этой проблеме, прочитайте проблему (закрыто) Python Bugtracker .

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

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

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

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

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

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

21 голосов
/ 24 августа 2011

Вы действительно можете, в зависимости от реализации. 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 также возможно создать объект итератора для имитации выбираемого генератора.

...