2009-08-28 3 views
6

(Вы можете прочитать this вопрос для некоторого фона)Изящно разрушающее травление в Python

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

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

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

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

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


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

+1

Java Beans .. Python Pickles .. Я хотел бы задушить ботаников, которые придумали это cutesy stuff –

ответ

0

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

Хм, что-то вроде этого?

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 

Очевидно, что это будет идти в расширение класса рассола с Переопределённым методом самосвала ...

3

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

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

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

+0

@Alex Martelli: Когда я использую copy_reg.pickle, какова область, в которой это изменение будет иметь значение? Я хочу, чтобы люди могли импортировать мою работу, не меняя системных значений, которые могли бы повредить их программу. –

+0

'copy_reg' является глобальным. Но если они в настоящее время не собирают модули (что невозможно по умолчанию по умолчанию), он не может «разрушить свою программу», чтобы сделать модули разборчивыми. –

+0

@Alex Martelli: Но если бы они столкнулись с одной и той же проблемой и определили макет канала по-другому, у нас возникла бы проблема. Я верю в вежливость и не изменяю состояние системы. Я считаю, что при импорте некоторого модуля в Python вам не придется беспокоиться о том, что он возится с глобальными глобальными системами вашей системы, и что важно иметь инструменты, которые позволят вам избежать такой «нечестивости» в ваших модулях. –

Смежные вопросы