代码之家  ›  专栏  ›  技术社区  ›  Mark Cramer

Python Pickle在Windows上失败,但在Mac上工作

  •  0
  • Mark Cramer  · 技术社区  · 7 年前

    我有课 MyClass 定义于 my_module . 类名 他有办法 pickle_myself 这会对相关类的实例进行pickle处理:

    def pickle_myself(self, pkl_file_path):
        with open(pkl_file_path, 'w+') as f:
            pkl.dump(self, f, protocol=2)
    

    我已经确定了 my_模块 在 PYTHONPATH .在翻译中,执行 __import__('my_module') 很好:

    >>> __import__('my_module')
    <module 'my_module' from 'A:\my_stuff\my_module.pyc'>
    

    然而,当最终加载文件时,我得到:

    File "A:\Anaconda\lib\pickle.py", line 1128, in find_class
      __import__(module)
    ImportError: No module named my_module
    

    我已经确定了一些事情:


    编辑——再现错误的玩具示例:

    这个例子本身就是一堆文件。

    首先,我们有一个模块 ball (存储在名为 ball.py ):

    class Ball():
        def __init__(self, ball_radius):
            self.ball_radius = ball_radius
    
        def say_hello(self):
            print "Hi, I'm a ball with radius {}!".format(self.ball_radius)
    

    然后,我们有了模块 test_environment :

    import os
    import ball
    #import dill as pkl
    import pickle as pkl
    
    class Environment():
        def __init__(self, store_dir, num_balls, default_ball_radius):
            self.store_dir = store_dir
            self.balls_in_environment = [ball.Ball(default_ball_radius) for x in range(num_balls)]
    
        def persist(self):
            pkl_file_path = os.path.join(self.store_dir, "test_stored_env.pkl")
    
            with open(pkl_file_path, 'w+') as f:
                pkl.dump(self, f, protocol=2)
    

    然后,我们有一个模块,它具有创建环境、持久化环境和加载环境的功能,名为 make_persist_load :

    import os
    import test_environment
    #import pickle as pkl
    import dill as pkl
    
    
    def make_env_and_persist():
        cwd = os.getcwd()
    
        my_env = test_environment.Environment(cwd, 5, 5)
    
        my_env.persist()
    
    def load_env(store_path):
        stored_env = None
    
        with open(store_path, 'rb') as pkl_f:
            stored_env = pkl.load(pkl_f)
    
        return stored_env
    

    然后我们有一个脚本来把所有的东西放在一起 test_serialization.py :

    import os
    import make_persist_load
    
    MAKE_AND_PERSIST = True
    LOAD = (not MAKE_AND_PERSIST)
    
    cwd = os.getcwd()
    store_path = os.path.join(cwd, "test_stored_env.pkl")
    
    if MAKE_AND_PERSIST == True:
        make_persist_load.make_env_and_persist()
    
    if LOAD == True:
        loaded_env = make_persist_load.load_env(store_path)
    

    为了便于使用这个玩具示例, I have put it all up on in a Github repository that simply needs to be cloned into your directory of choice. .请看 README 包含说明,我也在这里复制:

    说明:

    1) 将存储库克隆到目录中。

    2) 将存储库目录添加到PYTHONPATH。

    3) 打开 测试_序列化。py ,并设置变量 MAKE_AND_PERSIST 到 True .在解释器中运行脚本。

    4) 关闭上一个解释器实例,然后启动一个新实例。在里面 测试_序列化。py 改变 让你和你坚持下去 到 False ,这将以编程方式设置 LOAD 到 符合事实的 .在解释器中运行脚本,导致 ImportError: No module named test_environment .

    5) 默认情况下,测试设置为使用dill,而不是pickle。为了改变这一点,进入 test_environment.py 和 make_persist_load.py ,以根据需要更改导入。


    编辑:切换到dill'0.2.5后。dev0', dill.detect.trace(True) 输出

    C2: test_environment.Environment
    # C2
    D2: <dict object at 0x000000000A9BDAE8>
    C2: ball.Ball
    # C2
    D2: <dict object at 0x000000000AA25048>
    # D2
    D2: <dict object at 0x000000000AA25268>
    # D2
    D2: <dict object at 0x000000000A9BD598>
    # D2
    D2: <dict object at 0x000000000A9BD9D8>
    # D2
    D2: <dict object at 0x000000000A9B0BF8>
    # D2
    # D2
    

    编辑: 这个玩具示例在Mac/Ubuntu上运行时效果非常好(例如,类似Unix的系统?)。它只在Windows上失败。

    1 回复  |  直到 9 年前
        1
  •  4
  •   Community Mohan Dere    9 年前

    我可以从你的问题中看出,你可能正在做类似的事情,使用一个试图pickle类实例的类方法。这样做是不明智的,如果你这样做的话,使用它更明智 pkl.dump 而不是在课堂之外(在哪里 pkl 是 pickle 或 dill 等等)。然而,它 可以 仍使用此设计,请参见以下内容:

    >>> class Thing(object):
    ...   def pickle_myself(self, pkl_file_path):
    ...     with open(pkl_file_path, 'w+') as f:
    ...       pkl.dump(self, f, protocol=2)
    ... 
    >>> import dill as pkl
    >>> 
    >>> t = Thing()
    >>> t.pickle_myself('foo.pkl')
    

    然后重新启动。。。

    Python 2.7.10 (default, Sep  2 2015, 17:36:25) 
    [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import dill
    >>> f = open('foo.pkl', 'r')
    >>> t = dill.load(f)
    >>> t
    <__main__.Thing object at 0x1060ff410>
    

    如果你有一个更复杂的类,我相信你是这样做的,那么你很可能会遇到麻烦,尤其是如果这个类使用了同一目录中的另一个文件。

    >>> import dill
    >>> from bar import Zap
    >>> print dill.source.getsource(Zap)
    class Zap(object):
        x = 1
        def __init__(self, y):
            self.y = y
    
    >>> 
    >>> class Thing2(Zap):   
    ...   def pickle_myself(self, pkl_file_path):
    ...     with open(pkl_file_path, 'w+') as f:
    ...       dill.dump(self, f, protocol=2)
    ... 
    >>> t = Thing2(2)
    >>> t.pickle_myself('foo2.pkl')
    

    然后重新开始

    Python 2.7.10 (default, Sep  2 2015, 17:36:25) 
    [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import dill
    >>> f = open('foo2.pkl', 'r')
    >>> t = dill.load(f)
    >>> t
    <__main__.Thing2 object at 0x10eca8090>
    >>> t.y
    2
    >>> 
    

    好吧,那也行。你必须发布你的代码,这样我们才能看到你使用的是什么模式 小茴香 (及 泡菜 )失败了。我知道,让一个模块导入另一个未“安装”(即在某个本地目录中)的模块,并期望序列化“正常工作”并不适用于所有情况。

    看见 小茴香 问题: https://github.com/uqfoundation/dill/issues/128 https://github.com/uqfoundation/dill/issues/129 所以这个问题: Why dill dumps external classes by reference, no matter what? 对于一些失败的例子和潜在的解决方法。

    编辑 关于最新问题:

    我看不出你的问题。从命令行运行,从解释器导入( import test_serialization ),并在解释器中运行脚本(如下所示,并在步骤3-5中指出),所有这些都可以工作。这让我觉得你可能用的是旧版本的 小茴香 ?

    >>> import os
    >>> import make_persist_load
    >>> 
    >>> MAKE_AND_PERSIST = False #True
    >>> LOAD = (not MAKE_AND_PERSIST)
    >>> 
    >>> cwd = os.getcwd()
    >>> store_path = os.path.join(cwd, "test_stored_env.pkl")
    >>> 
    >>> if MAKE_AND_PERSIST == True:
    ...     make_persist_load.make_env_and_persist()
    ... 
    >>> if LOAD == True:
    ...     loaded_env = make_persist_load.load_env(store_path)
    ... 
    >>> 
    

    编辑 根据评论中的讨论:

    看起来这可能是Windows的问题,因为这似乎是唯一出现错误的操作系统。

    编辑 完成一些工作后(见: https://github.com/uqfoundation/dill/issues/140 ):

    使用这个最小的例子,我可以在Windows上重现相同的错误,而在MacOSX上仍然有效

    # test.py
    class Environment():
        def __init__(self):
            pass
    

    和

    # doit.py
    import test
    import dill
    
    env = test.Environment()
    path = "test.pkl"
    with open(path, 'w+') as f:
        dill.dump(env, f)
    
    with open(path, 'rb') as _f:
        _env = dill.load(_f)
        print _env
    

    然而,如果你使用 open(path, 'r') as _f ,它可以在Windows和MacOSX上运行。看起来像是 __import__ 在Windows系统上比在非Windows系统上对文件类型更敏感。尽管如此,还是扔了一个 ImportError 这很奇怪,但这一点小小的改变应该可以让它发挥作用。

        2
  •  3
  •   MMSA    9 年前

    如果有人遇到了同样的问题,我在运行Python 2.7时遇到了同样的问题,问题是在运行Linux时在windows上创建的pickle文件,我需要做的是运行 dos2unix 必须先使用

    sudo yum install dos2unix
    

    然后需要转换pickle文件示例

    dos2unix data.p