Вы можете решить и реализовать, как любой ранее неподдающийся анализу тип будет обрабатываться и распаковываться: см. модуль стандартной библиотеки copy_reg (переименован в copyreg
в Python 3.*).
По сути, вам нужно предоставить функцию, которая, учитывая экземпляр типа, сводит его к кортежу — с тем же протоколом, что и reduce специальный метод (за исключением того, что специальный метод сокращения не принимает аргументов, поскольку, когда он предоставляется, он вызывается непосредственно для объекта, в то время как функция вы предоставите объект в качестве единственного аргумента).
Как правило, кортеж, который вы возвращаете, имеет 2 элемента: вызываемый объект и кортеж аргументов для передачи. Вызываемый объект должен быть зарегистрирован как «безопасный конструктор» или, что то же самое, иметь атрибут __safe_for_unpickling__
со значением true. Эти элементы будут обработаны, и во время распаковки вызываемый объект будет вызываться с заданными аргументами и должен возвращать невыбранный объект.
Например, предположим, что вы хотите просто собирать модули по имени, так что их распаковка просто означает их повторный импорт (т. е. предположим для простоты, что вам не нужны динамически изменяемые модули, вложенные пакеты и т. модули). Затем:
>>> import sys, pickle, copy_reg
>>> def savemodule(module):
... return __import__, (module.__name__,)
...
>>> copy_reg.pickle(type(sys), savemodule)
>>> s = pickle.dumps(sys)
>>> s
"c__builtin__\n__import__\np0\n(S'sys'\np1\ntp2\nRp3\n."
>>> z = pickle.loads(s)
>>> z
<module 'sys' (built-in)>
Я использую старомодную ASCII-форму команды pickle, чтобы s
, строку, содержащую команду pickle, было легко проверить: она указывает распаковке вызвать встроенную функцию импорта со строкой sys
в качестве единственного аргумента. И z
показывает, что это действительно возвращает нам встроенный sys
модуль в результате распаковки, как и хотелось.
Теперь вам придется сделать вещи немного сложнее, чем просто __import__
(вам придется иметь дело с сохранением и восстановлением динамических изменений, навигацией по вложенному пространству имен и т. д.), и поэтому вам также придется вызывать copy_reg.constructor
(передавая в качестве аргумента вашей собственной функции, которая выполняет эту работу) перед тем, как вы copy_reg
сохраните модуль, который возвращает другую вашу функцию (и, если в отдельном запуске, также перед тем, как вы распарываете те огурцы, которые вы сделали, используя указанную функцию). Но я надеюсь, что эти простые случаи помогут показать, что на самом деле в этом нет ничего «внутренне» сложного!-)
person
Alex Martelli
schedule
28.08.2009