Изящно-унизительное маринование в Python - PullRequest
6 голосов
/ 28 августа 2009

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

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

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

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

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

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


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

Ответы [ 3 ]

3 голосов
/ 29 августа 2009

Вы можете решить и реализовать способ, с помощью которого любой ранее невыбираемый тип выбирается и выбирается: см. Стандартный библиотечный модуль 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)>

Я использую старомодную форму pickle ASCII, так что s, строку, содержащую pickle, легко исследовать: она дает команду unpickling вызвать встроенную функцию импорта со строкой sys в качестве единственного аргумента. И z показывает, что это действительно возвращает нам встроенный модуль sys в результате расслоения, как требуется.

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

0 голосов
/ 28 августа 2009

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

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 ...

0 голосов
/ 28 августа 2009

Как насчет следующего, который является оберткой, которую вы можете использовать, чтобы обернуть некоторые модули (возможно, любой модуль) во что-то, что можно засолить. Затем вы можете создать подкласс объекта 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)
...