Python: проверка того, является ли объект атомарным макетированием

Каков точный способ проверки того, может ли объект распыляться атомом? Когда я говорю «атомарно маринованный», я имею в виду, не рассматривая другие объекты, на которые он может ссылаться. Например, этот список:

l = [threading.Lock()] 

не является абордируемым объектом, потому что он относится к Lock которая не является подходящей. Но атомарно, этот список сам по себе разборчив.

Итак, как вы проверяете, является ли объект атомарным? (Я предполагаю, что проверка должна проводиться в классе, но я не уверен.)

Я хочу, чтобы он вел себя так:

 >>> is_atomically_pickleable(3) True >>> is_atomically_pickleable(3.1) True >>> is_atomically_pickleable([1, 2, 3]) True >>> is_atomically_pickleable(threading.Lock()) False >>> is_atomically_pickleable(open('whatever', 'r')) False 

И т.п.

5 Solutions collect form web for “Python: проверка того, является ли объект атомарным макетированием”

Учитывая, что вы готовы нарушить инкапсуляцию, я думаю, что это лучшее, что вы можете сделать:

 from pickle import Pickler import os class AtomicPickler(Pickler): def __init__(self, protocol): # You may want to replace this with a fake file object that just # discards writes. blackhole = open(os.devnull, 'w') Pickler.__init__(self, blackhole, protocol) self.depth = 0 def save(self, o): self.depth += 1 if self.depth == 1: return Pickler.save(self, o) self.depth -= 1 return def is_atomically_pickleable(o, protocol=None): pickler = AtomicPickler(protocol) try: pickler.dump(o) return True except: # Hopefully this exception was actually caused by dump(), and not # something like a KeyboardInterrupt return False 

В Python единственный способ узнать, что-то будет работать, – попробовать его. Такова природа языка как динамичная, как Python. Трудность с вашим вопросом заключается в том, что вы хотите различать неудачи на «верхнем уровне» и сбои на более глубоких уровнях.

Pickler.save – это, по сути, контрольный центр для Pickler.save логики Python, поэтому вышеописанное создает модифицированный Pickler который игнорирует рекурсивные вызовы его методу save . Любое исключение, возникающее при сохранении верхнего уровня, рассматривается как отказ травления. Вы можете добавить квалификаторы в оператор except . Неквалифицированные excepts в Python, как правило, представляют собой плохую идею, поскольку исключения используются не только для программных ошибок, но и для таких вещей, как KeyboardInterrupt и SystemExit .

Это может дать то, что может быть ложным отрицанием для типов с нечетной индивидуальной логикой травления. Например, если вы создаете собственный класс, подобный классу, который вместо того, чтобы вызывать рекурсивный вызов Pickler.save он фактически попытался самостоятельно определить свои элементы самостоятельно, а затем создал экземпляр этого класса, в котором содержится элемент, логика не могла is_atomically_pickleable , is_atomically_pickleable вернет False для этого экземпляра, даже если удаление элемента-нарушителя приведет к тому, что объект будет разборчивым.

Также обратите внимание на аргумент протокола is_atomically_pickleable . Теоретически объект может вести себя по-разному при мариновании с различными протоколами (хотя это было бы довольно странно), поэтому вы должны сделать это совпадение с аргументом протокола, который вы даете dump .

Учитывая динамический характер Python, я не думаю, что есть действительно определенный способ сделать то, что вы просите, помимо эвристики или белого списка.

Если я говорю:

 x = object() 

x «атомарно маятнико»? Что, если я скажу:

 x.foo = threading.Lock() 

? теперь x «атомарно маятниковая»?

Что делать, если я создал отдельный класс, который всегда имел атрибут блокировки? Что, если я удалю этот атрибут из экземпляра?

Я думаю, что интерфейс persistent_id плохо подходит для вас, как вы пытаетесь сделать. Он предназначен для использования, когда ваш объект должен ссылаться на эквивалентные объекты в новой программе, а не на копии старого. Вы пытаетесь отфильтровать каждый объект, который не может быть маринован, и что вы пытаетесь сделать это.

Я думаю, что это признак проблемы в вашем коде. Тот факт, что вы хотите рассортировать объекты, относящиеся к виджетам, файлам и замкам gui, говорит о том, что вы делаете что-то странное. Тип объектов, которые вы обычно сохраняете, не должен быть связан с ссылками на этот объект.

Сказав это, я думаю, что ваш лучший вариант заключается в следующем:

 class MyPickler(Pickler): def save(self, obj): try: Pickler.save(self, obj) except PicklingEror: Pickle.save( self, FilteredObject(obj) ) 

Это должно работать на реализацию python, я не делаю никаких гарантий относительно того, что произойдет в реализации C. Каждый объект, который будет сохранен, будет передан методу сохранения. Этот метод будет поднимать PicklingError, когда он не может разжечь объект. На этом этапе вы можете войти и вызвать функцию, прося ее рассолить свой собственный объект, который должен хорошо солить.

РЕДАКТИРОВАТЬ

По моему мнению, у вас есть, по сути, пользовательский словарь объектов. Некоторые объекты являются picklable, а некоторые – нет. Я бы сделал это:

 class saveable_dict(dict): def __getstate__(self): data = {} for key, value in self.items(): try: encoded = cPickle.dumps(value) except PicklingError: encoded = cPickle.dumps( Unpickable() ) return data def __setstate__(self, state): for key, value in state: self[key] = cPickle.loads(value) 

Затем используйте этот словарь, если вы хотите сохранить эту коллекцию объектов. Пользователь должен иметь возможность возвращать любые отбираемые объекты, но все остальное будет возвращено как объект Unpicklable (). Разница между этим и предыдущим подходом заключается в объектах, которые сами подбираются, но имеют ссылки на непривычные объекты. Но эти объекты, вероятно, собираются вернуться, несмотря на это.

Этот подход также имеет то преимущество, что он остается полностью в пределах определенного API и, следовательно, должен работать либо в cPickle, либо в pickle.

Я закончил тем, что сделал свое собственное решение.

Вот код . Вот тесты . Это часть GarlicSim , поэтому вы можете использовать ее, установив garlicsim и сделав from garlicsim.general_misc import pickle_tools .

Если вы хотите использовать его в коде Python 3, используйте вилку Python 3 для garlicsim .

Вот выдержка из модуля (может быть устаревшей):

 import re import cPickle as pickle_module import pickle # Importing just to get dispatch table, not pickling with it. import copy_reg import types from garlicsim.general_misc import address_tools from garlicsim.general_misc import misc_tools def is_atomically_pickleable(thing): ''' Return whether `thing` is an atomically pickleable object. "Atomically-pickleable" means that it's pickleable without considering any other object that it contains or refers to. For example, a `list` is atomically pickleable, even if it contains an unpickleable object, like a `threading.Lock()`. However, the `threading.Lock()` itself is not atomically pickleable. ''' my_type = misc_tools.get_actual_type(thing) return _is_type_atomically_pickleable(my_type, thing) def _is_type_atomically_pickleable(type_, thing=None): '''Return whether `type_` is an atomically pickleable type.''' try: return _is_type_atomically_pickleable.cache[type_] except KeyError: pass if thing is not None: assert isinstance(thing, type_) # Sub-function in order to do caching without crowding the main algorithm: def get_result(): # We allow a flag for types to painlessly declare whether they're # atomically pickleable: if hasattr(type_, '_is_atomically_pickleable'): return type_._is_atomically_pickleable # Weird special case: `threading.Lock` objects don't have `__class__`. # We assume that objects that don't have `__class__` can't be pickled. # (With the exception of old-style classes themselves.) if not hasattr(thing, '__class__') and \ (not isinstance(thing, types.ClassType)): return False if not issubclass(type_, object): return True def assert_legit_pickling_exception(exception): '''Assert that `exception` reports a problem in pickling.''' message = exception.args[0] segments = [ "can't pickle", 'should only be shared between processes through inheritance', 'cannot be passed between processes or pickled' ] assert any((segment in message) for segment in segments) # todo: turn to warning if type_ in pickle.Pickler.dispatch: return True reduce_function = copy_reg.dispatch_table.get(type_) if reduce_function: try: reduce_result = reduce_function(thing) except Exception, exception: assert_legit_pickling_exception(exception) return False else: return True reduce_function = getattr(type_, '__reduce_ex__', None) if reduce_function: try: reduce_result = reduce_function(thing, 0) # (The `0` is the protocol argument.) except Exception, exception: assert_legit_pickling_exception(exception) return False else: return True reduce_function = getattr(type_, '__reduce__', None) if reduce_function: try: reduce_result = reduce_function(thing) except Exception, exception: assert_legit_pickling_exception(exception) return False else: return True return False result = get_result() _is_type_atomically_pickleable.cache[type_] = result return result _is_type_atomically_pickleable.cache = {} 

dill имеет метод pickles для такой проверки.

 >>> import threading >>> l = [threading.Lock()] >>> >>> import dill >>> dill.pickles(l) True >>> >>> dill.pickles(threading.Lock()) True >>> f = open('whatever', 'w') >>> f.close() >>> dill.pickles(open('whatever', 'r')) True 

Ну, dill атомизирует все ваши примеры, поэтому давайте попробуем что-то еще:

 >>> l = [iter([1,2,3]), xrange(5)] >>> dill.pickles(l) False 

Хорошо, это не удается. Теперь давайте расследуем:

 >>> dill.detect.trace(True) >>> dill.pickles(l) T4: <type 'listiterator'> False >>> map(dill.pickles, l) T4: <type 'listiterator'> Si: xrange(5) F2: <function _eval_repr at 0x106991cf8> [False, True] 

ОК. мы можем видеть, что iter терпит неудачу, но xrange делает pickle. Итак, давайте заменим iter .

 >>> l[0] = xrange(1,4) >>> dill.pickles(l) Si: xrange(1, 4) F2: <function _eval_repr at 0x106991cf8> Si: xrange(5) True 

Теперь наш объект атомизируется.

  • _pickle в python3 не работает для большой экономии данных
  • ошибка травления в python?
  • Я продолжаю получать «недопустимый символ в идентификаторе» при открытии файла в python
  • Соленые огурцы: Почему они так называли?
  • Как проверить, был ли файл создан рассолом?
  • Почему возникает неожиданное поведение на Python после травления?
  • Травление питона после изменения каталога модуля
  • Python: pickle.loads не удалось выполнить экземпляр класса
  • травление и расчистка пользовательского класса
  • Сосредоточенные объекты
  • Ошибка Pilton Pickling Slots
  • Python - лучший язык программирования в мире.