热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

读取pickle文件时的AttributeError

如何解决《读取pickle文件时的AttributeError》经验,为你挑选了1个好方法。

当我在spyder(python 3.6.5)上读取我的.pkl文件时出现以下错误:

1
2
3
4
5
6
7
8
9
10
IN: with open(file, "rb") as f:
       data = pickle.load(f) 
 
Traceback (most recent call last):
 
 File "<ipython-input-5-d9796b902b88>", line 2, in <module>
   data = pickle.load(f)
 
AttributeError: Can't get attribute 'Signal' on <module '__main__'="" from="" 'c:\\python36\\lib\\site-packages\\spyder\\utils\\ipython\\start_kernel.py'="">
</module></module></ipython-input-5-d9796b902b88>

上下文:

我的程序由一个文件组成:program.py 在程序中,Signal定义了一个类以及许多函数.下面提供了该计划的简要概述:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
import _pickle as pickle
import os
 
# The unique class
class Signal:
    def __init__(self, fq, t0, tf):
        self.fq = fq
        self.t0 = t0
        self.tf = tf
        self.timeline = np.round(np.arange(t0, tf, 1/fq*1000), 3)
 
# The functions
def write_file(data, folder_path, file_name):
    with open(join(folder_path, file_name), "wb") as output:
        pickle.dump(data, output, -1)
 
def read_file(folder_path, file_name):
    with open(join(folder_path, file_name), "rb") as input:
        data= pickle.load(input)
    return data
 
def compute_data(# parameters):
    # do stuff

该函数compute_data将返回表单元组的列表:

1
data = [((Signal_1_1, Signal_1_2, ...), val 1), ((Signal_2_1, Signal_2_2, ...), val 2)...]

当然,Signal_i_k是一个对象Signal.此列表将以.pkl格式保存.而且,我正在使用不同的compute_data函数参数进行大量迭代.许多迭代将使用过去的计算数据作为起点,因此将读取相应的和所需的.pkl文件.

最后,我同时使用多台计算机,每台计算机都将计算数据保存在本地网络上.因此,每台计算机都可以访问其他计算机生成的数据并将其用作起点.

回到错误:

我的主要问题是,当我通过双击文件或通过windows cmd或PowerShell启动程序时,我从未遇到此错误.该程序永远不会崩溃抛出此错误并运行没有明显的问题.

但是,我无法在spyder中读取.pkl文件.每次我尝试,都会抛出错误.

知道为什么我有这种奇怪的行为吗?

谢谢!



1> Dunes..:

当你把东西转储成a时,pickle你应该避免在主模块中声明的pickling类和函数.您的问题是(部分)因为您的程序中只有一个文件.pickle是惰性的,不会序列化类定义或函数定义.相反,它保存了如何查找类(它所在的模块及其名称)的参考.

当python直接运行脚本/文件时,它将程序作为__main__模块运行(无论其实际文件名如何).但是,当加载文件而不是主模块时(例如,当您执行类似操作时import program),其模块名称将基于其名称.所以program.py被召唤program.

当您从命令行运行时,您正在执行前者,并调用该模块__main__.因此,pickle会创建对类的引用__main__.Signal.当spyder尝试加载pickle文件时,它会被告知导入__main__并查找Signal.但是,spyder的__main__模块是用来启动spyder而不是你的模块,program.py所以pickle找不到Signal.

您可以通过运行检查pickle文件的内容(-a打印每个命令的描述).从这里你会看到你的类被引用为__main__.Signal.

1
python -m pickletools -a file.pkl

你会看到类似的东西:

1
2
3
4
5
6
7
8
9
10
    0: \x80 PROTO      3              Protocol version indicator.
    2: c    GLOBAL     '__main__ Signal' Push a global object (module.attr) on the stack.
   19: q    BINPUT     0                 Store the stack top into the memo.  The stack is not popped.
   21: )    EMPTY_TUPLE                  Push an empty tuple.
   22: \x81 NEWOBJ                       Build an object instance.
   23: q    BINPUT     1                 Store the stack top into the memo.  The stack is not popped.
   ...
   51: b    BUILD                        Finish building an object, via __setstate__ or dict update.
   52: .    STOP                         Stop the unpickling machine.
highest protocol among opcodes = 2
解决方案

您可以使用多种解决方案:

    不要序列化__main__模块中定义的类的实例.最简单,最好的解决方案.而是将这些类移动到另一个模块,或编写一个main.py脚本来调用您的程序(两者都意味着在__main__模块中不再找到这样的类).

    写一个定制的derserialiser

    编写自定义序列化程序

以下解决方案将使用out.pkl由以下代码创建的pickle文件(在名为的文件中program.py):

1
2
3
4
5
6
7
8
9
10
import pickle
 
class MyClass:
    def __init__(self, name):
        self.name = name
 
if __name__ == '__main__':
    o = MyClass('test')
    with open('out.pkl', 'wb') as f:
        pickle.dump(o, f)

Custom Deserialiser解决方案

您可以编写一个客户反序列化器,它知道何时遇到__main__模块的引用您真正的意思是program模块.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pickle
 
class MyCustomUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == "__main__":
            module = "program"
        return super().find_class(module, name)
 
with open('out.pkl', 'rb') as f:
    unpickler = MyCustomUnpickler(f)
    obj = unpickler.load()
 
print(obj)
print(obj.name)

这是加载已创建的pickle文件的最简单方法.该程序是它将责任推到反序列化代码上,当序列化代码真正负责正确创建pickle文件时.

自定义序列化解决方案

与之前的解决方案相比,您可以确保任何人都可以轻松地对序列化的pickle对象进行反序列化,而无需了解自定义反序列化逻辑.为此,您可以使用该copyreg模块来通知pickle如何反序列化各种类.所以在这里,你要做的就是告诉pickle我们将所有__main__类的实例反序列化,就好像它们是program类的实例一样.您需要为每个类注册一个自定义序列化程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import program
import pickle
import copyreg
 
class MyClass:
    def __init__(self, name):
        self.name = name
 
def pickle_MyClass(obj):
    assert type(obj) is MyClass
    return program.MyClass, (obj.name,)
 
copyreg.pickle(MyClass, pickle_MyClass)
 
if __name__ == '__main__':
    o = MyClass('test')
    with open('out.pkl', 'wb') as f:
        pickle.dump(o, f)


推荐阅读
  • 本文详细解析了Python中的os和sys模块,介绍了它们的功能、常用方法及其在实际编程中的应用。 ... [详细]
  • 本文详细介绍如何使用Python进行配置文件的读写操作,涵盖常见的配置文件格式(如INI、JSON、TOML和YAML),并提供具体的代码示例。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • 深入理解Tornado模板系统
    本文详细介绍了Tornado框架中模板系统的使用方法。Tornado自带的轻量级、高效且灵活的模板语言位于tornado.template模块,支持嵌入Python代码片段,帮助开发者快速构建动态网页。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • Python自动化处理:从Word文档提取内容并生成带水印的PDF
    本文介绍如何利用Python实现从特定网站下载Word文档,去除水印并添加自定义水印,最终将文档转换为PDF格式。该方法适用于批量处理和自动化需求。 ... [详细]
  • 掌握远程执行Linux脚本和命令的技巧
    本文将详细介绍如何利用Python的Paramiko库实现远程执行Linux脚本和命令,帮助读者快速掌握这一实用技能。通过具体的示例和详尽的解释,让初学者也能轻松上手。 ... [详细]
  • 本文介绍如何使用 Python 提取和替换 .docx 文件中的图片。.docx 文件本质上是压缩文件,通过解压可以访问其中的图片资源。此外,我们还将探讨使用第三方库 docx 的方法来简化这一过程。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • CentOS7源码编译安装MySQL5.6
    2019独角兽企业重金招聘Python工程师标准一、先在cmake官网下个最新的cmake源码包cmake官网:https:www.cmake.org如此时最新 ... [详细]
  • 本文详细介绍了如何解决Uploadify插件在Internet Explorer(IE)9和10版本中遇到的点击失效及JQuery运行时错误问题。通过修改相关JavaScript代码,确保上传功能在不同浏览器环境中的一致性和稳定性。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 前言--页数多了以后需要指定到某一页(只做了功能,样式没有细调)html ... [详细]
author-avatar
留心6_136
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有