Экземпляр класса Pickle плюс определение?

Это проблема, которая, как я подозреваю, распространена, но я не нашел для нее решения. То, что я хочу, довольно просто и, по-видимому, технически осуществимо: у меня есть простой класс Python, и я хочу сохранить его на диске, экземпляр и определение в одном файле. Pickle сохранит данные, но не сохранит определение класса. Кто-то может возразить, что определение класса уже хранится в моем файле .py, но мне не нужен отдельный файл .py; моя цель состоит в том, чтобы иметь автономный единый файл, который я мог бы вернуть в свое пространство имен с помощью одной строки кода.

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

Таким образом, удобство заключается не столько в удобочитаемости кода, сколько в легкой и четкой связи между кодом и данными. Мне это кажется достойной целью, хотя я понимаю, что в большинстве ситуаций она неуместна.

Итак, вопрос: есть ли пакет или фрагмент кода, который делает такую ​​​​вещь, потому что я не могу найти его.


person Eelco Hoogendoorn    schedule 17.07.2011    source источник
comment
Я бы рекомендовал избегать рассола для длительного хранения данных: он очень хрупкий. Попробуйте использовать dict с json или HDF5 с h5python. Это не отвечает на ваш вопрос, так что это комментарий, но я искренне считаю, что это более жизнеспособное долгосрочное решение.   -  person Seth Johnson    schedule 17.07.2011
comment
Хм, именно такой самодокументирующийся характер в долгосрочной перспективе я и искал. Обратите внимание, что неизменяемый код является неотъемлемой частью всей схемы; я что-то заскриптовал и хочу проверить через месяц; Первоначальный сценарий может быть утерян, мне все равно, но возвращение myobject.myattribute должно быть полностью самодокументированным для моих нужд.   -  person Eelco Hoogendoorn    schedule 18.07.2011
comment
В дальнейшем; JSON - это текст (нет?), который был бы крайне неэффективным, и HDF5 требует, чтобы я сохранял формат файла, если мне нужно будет изменить интерпретацию этих данных позже, чего я стараюсь избегать.   -  person Eelco Hoogendoorn    schedule 18.07.2011
comment
Данные Pickle не являются самодокументируемыми: даже не гарантируется их корректность от одной версии Python к другой, на разных компьютерах и т. д. JSON — это текст, но скорость импорта может быть не самой большой проблемой. Я уверен, что с помощью JSON или HDF5 вы сможете найти разумный способ сохранения данных, чтобы их можно было расширить и прочитать позже.   -  person Seth Johnson    schedule 18.07.2011
comment
Атрибуты имеют имена; это вся документация, которую я прошу. Если бы только pickle сохранил имена этих атрибутов, мне не пришлось бы их перепечатывать. Меня не волнует интервалидность между версией Python или компьютерами, так как я единственный человек, который когда-либо будет возиться с этими файлами. Я не беспокоюсь о том, что мне придется пытаться открыть мой файл как с 2.6, так и с 2.7; что меня беспокоит, так это то, что я остаюсь с потоком байтов и не понимаю, что это значит. Скорость импорта и место для хранения являются проблемой, учитывая, что эти файлы обычно исчисляются гигабайтами. Можем ли мы теперь вернуться к ответу на мой вопрос?   -  person Eelco Hoogendoorn    schedule 19.07.2011
comment
поток байтов и непонимание того, что это значит, как раз и есть описание Пикла. Объекты Pickle — это байт-код (посмотрите, как работает Pickle): если вы измените свой класс любым количеством способов, этот поток кода будет бесполезен.   -  person Seth Johnson    schedule 19.07.2011


Ответы (2)


Если вы используете dill, это позволяет вам обращаться с __main__ так, как если бы это был модуль Python (по большей части). Следовательно, вы можете сериализовать интерактивно определенные классы и т.п. dill также (по умолчанию) может передавать определение класса как часть рассола.

>>> class MyTest(object):
...   def foo(self, x):
...     return self.x * x
...   x = 4
... 
>>> f = MyTest() 
>>> import dill
>>>
>>> with open('test.pkl', 'wb') as s:
...   dill.dump(f, s)
... 
>>> 

Затем выключите интерпретатор и отправьте файл test.pkl по TCP. На вашей удаленной машине теперь вы можете получить экземпляр класса.

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('test.pkl', 'rb') as s:
...   f = dill.load(s)
... 
>>> f
<__main__.MyTest object at 0x1069348d0>
>>> f.x
4
>>> f.foo(2)
8
>>>             

Но как получить определение класса? Так что это не совсем то, что вы хотели. Однако есть следующее.

>>> class MyTest2(object):
...   def bar(self, x):
...     return x*x + self.x
...   x = 1
... 
>>> import dill
>>> with open('test2.pkl', 'wb') as s:
...   dill.dump(MyTest2, s)
... 
>>>

Затем, после отправки файла… вы можете получить определение класса.

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('test2.pkl', 'rb') as s:
...   MyTest2 = dill.load(s)
... 
>>> print dill.source.getsource(MyTest2)
class MyTest2(object):
  def bar(self, x):
    return x*x + self.x
  x = 1

>>> f = MyTest2()
>>> f.x
1
>>> f.bar(4)
17

Поскольку вы искали один вкладыш, я могу сделать лучше. Я не показал, что вы можете отправить класс и экземпляр одновременно, и, возможно, это то, чего вы хотели.

>>> import dill
>>> class Foo(object): 
...   def bar(self, x):
...     return x+self.x
...   x = 1
... 
>>> b = Foo()
>>> b.x = 5
>>> 
>>> with open('blah.pkl', 'wb') as s:
...   dill.dump((Foo, b), s)
... 
>>> 

Тем не менее, это еще не одна строка, но она работает.

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> with open('blah.pkl', 'rb') as s:
...   Foo, b = dill.load(s)
... 
>>> b.x  
5
>>> Foo.bar(b, 2)
7

Итак, внутри dill есть dill.source, и у него есть методы, которые могут обнаруживать зависимости функций и классов и брать их вместе с рассолом (по большей части).

>>> def foo(x):
...   return x*x
... 
>>> class Bar(object):
...   def zap(self, x):
...     return foo(x) * self.x
...   x = 3
... 
>>> print dill.source.importable(Bar.zap, source=True)
def foo(x):
  return x*x
def zap(self, x):
  return foo(x) * self.x

Так что это не «идеально» (или, возможно, не то, что ожидалось)… но он сериализует код для динамически созданного метода и его зависимостей. Вы просто не получите остальную часть класса, но в этом случае остальная часть класса не нужна. Тем не менее, это не похоже на то, что вы хотели.

Если вы хотите получить все, вы можете просто замариновать всю сессию. И в одной строке (две, считая import).

>>> import dill
>>> def foo(x):
...   return x*x
... 
>>> class Blah(object):
...   def bar(self, x):
...     self.x = (lambda x:foo(x)+self.x)(x)
...   x = 2
... 
>>> b = Blah()
>>> b.x
2
>>> b.bar(3)
>>> b.x
11
>>> # the one line
>>> dill.dump_session('foo.pkl')
>>> 

Затем на удаленной машине...

Python 2.7.9 (default, Dec 11 2014, 01:21:43) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> # the one line
>>> dill.load_session('foo.pkl')
>>> b.x
11
>>> b.bar(2)
>>> b.x
15
>>> foo(3)
9

Наконец, если вы хотите, чтобы транспорт был «выполнен» для вас прозрачно (вместо использования файла), вы можете использовать pathos.pp или ppft, которые предоставляют возможность отправлять объекты на второй сервер python (на удаленной машине) или python процесс. Они используют dill под капотом и просто передают код по проводам.

>>> class More(object):
...   def squared(self, x):
...     return x*x
... 
>>> import pathos
>>> 
>>> p = pathos.pp.ParallelPythonPool(servers=('localhost,1234',))
>>> 
>>> m = More()
>>> p.map(m.squared, range(5))
[0, 1, 4, 9, 16]

Аргумент servers является необязательным, и здесь просто подключение к локальной машине через порт 1234… но если вы вместо этого используете имя и порт удаленной машины (или также), вы перейдете на удаленную машину — «без усилий» .

Получите dill, pathos и ppft здесь: https://github.com/uqfoundation

person Mike McKerns    schedule 22.01.2015
comment
Спасибо! Некоторое время не думал об этой проблеме (в последнее время не проводил так много исследований данных), но это хорошо решило мою первоначальную проблему. - person Eelco Hoogendoorn; 23.01.2015

Pickle не может распарывать код Python, поэтому я не думаю, что это вообще возможно с pickle.

>>> from pickle import *
>>> def A(object):
...     def __init__(self):
...             self.potato = "Hello"
...             print "Starting"
...                                                                                                                                                                  
>>> A.__code__                                                                                                                                                       
<code object A at 0xb76bc0b0, file "<stdin>", line 1>                                                                                                                
>>> dumps(A.__code__)                                                                                                                                                
Traceback (most recent call last):                                                                                                                                   
  File "<stdin>", line 1, in <module>                                                                                                                                
  File "/usr/lib/python2.6/pickle.py", line 1366, in dumps
    Pickler(file, protocol).dump(obj)
  File "/usr/lib/python2.6/pickle.py", line 224, in dump
    self.save(obj)
  File "/usr/lib/python2.6/pickle.py", line 306, in save
    rv = reduce(self.proto)
  File "/usr/lib/python2.6/copy_reg.py", line 70, in _reduce_ex
    raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle code objects
person Nick Craig-Wood    schedule 17.07.2011
comment
Первый комментарий здесь stackoverflow.com/questions/2626636/pickling-a- class-definition имеет ссылку на анализ всего состояния интерпретатора. Это не совсем то, что я хочу, но, похоже, под капотом он делает то, что меня интересует. Разве нельзя было бы автоматически получить строку, определяющую любой данный класс, а затем выбрать и оценить ее позже? - person Eelco Hoogendoorn; 18.07.2011