Изящно-унизительное маринование в Python

(Вы можете прочитать этот вопрос для некоторого фона)

Я хотел бы иметь изящно-унизительный способ выбора объектов в Python.

Выбирая объект, давайте назовем его основным объектом, иногда Средство выбора создает исключение, потому что он не может выбрать определенный подобъект главного объекта. Например, я часто получаю сообщение об ошибке "не могу выбрать объекты модуля". Это потому, что я ссылаюсь на модуль из основного объекта.

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

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

Есть что-нибудь подобное? У кого-нибудь есть идеи, как к этому подойти?


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

3 ответа

Вы можете решить и реализовать, каким образом любой ранее невыбираемый тип выбирается и выбирается: см. Стандартный библиотечный модуль copy_reg (переименован в copyreg в Python 3.*).

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

Как правило, возвращаемый вами кортеж имеет 2 элемента: вызываемый и набор аргументов для передачи ему. Вызываемый объект должен быть зарегистрирован как "безопасный конструктор" или иметь эквивалентный атрибут __safe_for_unpickling__ с истинным значением. Эти элементы будут засечены, и в момент разборки будет вызван вызываемый объект с заданными аргументами, и он должен вернуть неотобранный объект.

Например, предположим, что вы хотите просто выбрать модули по имени, так что удаление их означает просто их повторный импорт (т.е. для простоты предположим, что вам не нужны динамически модифицированные модули, вложенные пакеты и т. Д., Просто обычный верхний уровень). модули). Затем:

>>> 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, чтобы s Строка, содержащая параметр pickle, легко исследуется: она дает команду откреплению вызывать встроенную функцию импорта со строкой sys в качестве единственного аргумента. А также z показывает, что это действительно возвращает нам встроенный sys модуль в результате отжима, по желанию.

Теперь вам придется сделать вещи немного сложнее, чем просто __import__ (вам придется иметь дело с сохранением и восстановлением динамических изменений, перемещаться по вложенному пространству имен и т. д.), и, следовательно, вам также придется вызывать copy_reg.constructor (передавая в качестве аргумента вашу собственную функцию, которая выполняет эту работу) перед вами copy_reg функция сохранения модуля, которая возвращает вашу другую функцию (и, в случае отдельного прогона, также перед тем, как вы отрежете те соленья, которые вы сделали с помощью указанной функции). Но я надеюсь, что эти простые случаи помогают показать, что в этом нет ничего особенного, что вообще "по сути" сложно!-)

Хм, как то так?

import sys

attribList = dir(someobject)
for attrib in attribList:
    if(type(attrib) == type(sys)): #is a module
        #put in a facade, either recursively list the module and do the same thing, or just put in something like str('modulename_module')
    else:
        #proceed with normal pickle

Очевидно, это вошло бы в расширение класса pickle с помощью переопределенного метода dump...

Как насчет следующего, который является оберткой, которую вы можете использовать, чтобы обернуть некоторые модули (может быть, любой модуль) во что-то, что может быть засолено. Затем вы можете создать подкласс объекта Pickler, чтобы проверить, является ли целевой объект модулем, и если это так, обернуть его. Это выполняет то, что вы хотите?

class PickleableModuleWrapper(object):
    def __init__(self, module):
        # make a copy of the module's namespace in this instance
        self.__dict__ = dict(module.__dict__)
        # remove anything that's going to give us trouble during pickling
        self.remove_unpickleable_attributes()

    def remove_unpickleable_attributes(self):
        for name, value in self.__dict__.items():
            try:
                pickle.dumps(value)
            except Exception:
                del self.__dict__[name]

import pickle
p = pickle.dumps(PickleableModuleWrapper(pickle))
wrapped_mod = pickle.loads(p)
Другие вопросы по тегам