【问题标题】:Pickle class instance plus definition?Pickle 类实例加定义?
【发布时间】:2011-07-17 19:21:45
【问题描述】:

这是一个我怀疑很常见的问题,但我还没有找到解决方案。我想要的非常简单,而且在技术上看似可行:我有一个简单的 python 类,我想将它存储在磁盘上,实例和定义,在一个文件中。 Pickle 将存储数据,但不存储类定义。有人可能会争辩说类定义已经存储在我的 .py 文件中,但我不想要一个单独的 .py 文件;我的目标是拥有一个独立的单个文件,我可以通过一行代码将其弹回到我的命名空间中。

所以是的,我知道这可能使用两个文件和两行代码,但我希望它在一个文件和一行代码中。原因是因为我经常发现自己处于这种情况;我正在处理一些大数据集,在 python 中对其进行操作,然后必须将切片、切块和转换后的数据写回到一些预先存在的目录结构中。我不想要的是在这些数据目录中乱扔命名错误的 python 类存根以保持我的代码和数据相关联,而我更不想要的是跟踪和组织所有这些小的临时类定义的麻烦在脚本中独立运行。

因此,便利性不在于代码的可读性,而在于代码和数据之间毫不费力且不可篡改的关联。这对我来说似乎是一个有价值的目标,尽管我知道这在大多数情况下并不合适。

所以问题是:有没有一个包或代码sn-p可以做这样的事情,因为我似乎找不到。

【问题讨论】:

  • 我建议避免 pickle 用于长期数据存储:它非常脆弱。尝试使用带有 json 的 dict,或带有 h5python 的 HDF5。这不能回答你的问题,所以这是一个评论,但老实说,我认为这是一个更可行的长期解决方案。
  • 嗯,这正是我一直在寻找的长期自我记录的性质。请注意,不变的代码是整个方案的组成部分;我编写了一些脚本并想在一个月后检查一下;我只关心原始脚本可能已经消失了,但取回 myobject.myattribute 应该是为了满足我的需要而自我记录。
  • 进一步; JSON 是文本(不是吗?),效率极低,如果我以后要对解释该数据进行任何更改,HDF5 要求我存储文件格式,我试图避免这种情况。
  • Pickle 数据不是自记录的:它甚至不能保证从一个 Python 版本到另一个版本、跨计算机或任何东西都有效。 JSON 是文本,但导入速度可能不是您最关心的问题。使用 JSON 或 HDF5,我相信您可以找到一种合理的方法来保存数据,以便以后可以扩展和读取。
  • 属性有名字;这就是我要求的所有文件。如果只有 pickle 会保存这些属性名称,我就不必重新输入它们了。我不关心 python 版本或计算机之间的有效期,因为我是唯一一个会打扰这些文件的人。我不用担心必须尝试用 2.6 和 2.7 打开我的文件;我担心的是留下字节流并且不知道这意味着什么。考虑到这些文件通常会达到千兆字节,导入和存储空间的速度是一个问题。我们现在可以回去回答我的问题了吗?

标签: python pickle


【解决方案1】:

如果您使用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)
... 
>>> 

然后关闭解释器,并通过 TCP 发送文件test.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
>>> 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.ppppft,它们提供将对象发送到第二个 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 上的本地机器...但是如果您使用远程机器名称和端口代替(或同时使用),您将启动到远程机器——“毫不费力”。

在此处获取dillpathosppfthttps://github.com/uqfoundation

【讨论】:

  • 谢谢!暂时没有考虑这个问题(最近没有做这么多的数据探索),但这很好地解决了我最初的问题。
【解决方案2】:

Pickle 不能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

【讨论】:

猜你喜欢
  • 2019-01-20
  • 1970-01-01
  • 2021-12-18
  • 2013-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-22
  • 1970-01-01
相关资源
最近更新 更多