【问题标题】:Python pickle instance has no attributePython pickle 实例没有属性
【发布时间】:2015-03-22 18:05:37
【问题描述】:

我正在尝试抓取一个网站。在这个网站中,我将抓取的人员存储在 person_set 中,并将用于抓取下一个人员的队列存储在 parse_queue 中。在每个人爬取开始时,我需要将这两个数据结构写入一个文件,以防爬取因异常或连接不良而中断,以便稍后继续。

我有三个 python 文件。一个主文件、一个蜘蛛和一个人模型。 Main 实例化 spider,spider 开始解析并在必要时调用 write 和 read。 person 文件有 Person 类,它是存储人员数据的模型。

我在读取我写的数据时遇到问题。我检查了很多关于这个错误的问题,这似乎是一个导入问题。但即使我将 Person 类导入 main 和 spiders,它仍然给我错误。似乎emergency_read 方法不受我的顶级导入的影响。

main.py

from spiders import Spider
from person import Person
import pickle

def main():
    ....
    spider = Spider("seed_input")
    spider.parse(client)

spiders.py

import pickle
from person import Person

class Spider:

    def __init__(self, filename):
            self.person_set = Set()

            self.file_to_seed(filename)
            for seed_url in self.seed_list:
                    self.seed_to_parse_queue(seed_url)

    def parse(self, client):
        if os.path.exists('tmp.person_set'):
                print "Program wasnt ended properly, continuing from where it left"
                self.emergency_read()

        ... starts parsing

    def emergency_write(self):

        if os.path.exists('tmp.person_set'):
            self.delete_emergency_files()

        with open('tmp.person_set', 'wb') as f:
            pickle.dump(self.person_set, f)

        with open('tmp.parse_queue', 'wb') as f:
            pickle.dump(self.parse_queue, f)

    def emergency_read(self):
        with open('tmp.person_set', 'rb') as f:
            self.person_set = pickle.load(f)

        with open('tmp.parse_queue', 'rb') as f:
            self.parse_queue = pickle.load(f)

person.py

class Person:

    def __init__(self, name):
            self.name = name
            self.friend_set = Set()
            self.profile_url = ""
            self.id = 0
            self.color = "Grey"
            self.parent = None
            self.depth = 0

    def add_friend(self, friend):
            self.friend_set.add(friend)

    def __repr__(self):
            return "Person(%s, %s)" % (self.profile_url, self.name)

    def __eq__(self, other):
            if isinstance(other, Person):
                return ((self.profile_url == other.profile_url) and (self.name == other.name))
            else:
                return False

    def __ne__(self, other):
            return (not self.__eq__(other))

    def __hash__(self):
            return hash(self.__repr__())

堆栈跟踪

python main.py 
Program wasnt ended properly, continuing from where it left
Traceback (most recent call last):
File "main.py", line 47, in <module>
main()
File "main.py", line 34, in main
spider.parse(client)
File "/home/ynscn/py-workspace/lll/spiders.py", line 39, in parse
self.emergency_read()
File "/home/ynscn/py-workspace/lll/spiders.py", line 262, in emergency_read
self.person_set = pickle.load(f)
File "/usr/lib/python2.7/pickle.py", line 1378, in load
return Unpickler(file).load()
File "/usr/lib/python2.7/pickle.py", line 858, in load
dispatch[key](self)
File "/usr/lib/python2.7/pickle.py", line 1198, in load_setitem
dict[key] = value
File "/home/ynscn/py-workspace/lll/person.py", line 30, in __hash__
return hash(self.__repr__())
File "/home/ynscn/py-workspace/lll/person.py", line 18, in __repr__
return "Person(%s, %s)" % (self.profile_url, self.name)
AttributeError: Person instance has no attribute 'profile_url'

【问题讨论】:

  • 可以分享模块文件结构树吗?
  • 你能尝试核对你所有的 .pyc 文件并重新运行脚本吗?特别是person.pyc。也许有可能正在导入旧版本的模块(pyc)文件。 stackoverflow.com/questions/15839555/…
  • 我尝试核对 .pyc 文件,但没有成功

标签: python pickle


【解决方案1】:

Pickle 以不确定的顺序加载类实例的组件。此错误发生在加载期间,但在反序列化 Person.profile_url 属性之前。请注意,它在 load_setitem 期间失败,这意味着它可能正在尝试加载 friend_set 属性,这是一个集合。

您的自定义__repr__() 依赖于类属性,然后您的自定义__hash__()pickle 需要)依赖于__repr__()

我的建议是使用 Python 的默认 __hash__ 方法。这行得通吗?

【讨论】:

  • 删除散列方法有效,但我需要该散列方法通过查看他们的 profile_url 来将人员实例存储在一个集合中。我该怎么做?
  • 另一个想法是使用 JSON 而不是 pickle 进行序列化,只要 Person 仅包含内置类型:stackoverflow.com/questions/3768895/…
【解决方案2】:

如果您使用dill 而不是pickle,您的代码可能按原样序列化。 dill 可以腌制类对象、实例、方法和属性……以及 python 中的大多数东西。 dill 还可以存储类和类实例的动态修改状态。我同意这似乎是一个泡菜load 错误,正如@nofinator 指出的那样。但是,dill 可能会绕过它。

可能更好的是,如果您想强制执行加载和卸载命令,您可以尝试添加 __getstate____setstate__ 方法。

【讨论】:

  • Dill 也返回相同的错误,我想我必须用 JSON 实现我自己的存储逻辑。
  • 嗯,值得一试,我猜……但我确实认为 __getstate____setstate__ 为你工作。
猜你喜欢
  • 2012-10-07
  • 1970-01-01
  • 2013-10-05
  • 1970-01-01
  • 1970-01-01
  • 2013-04-30
  • 1970-01-01
  • 2013-01-09
  • 1970-01-01
相关资源
最近更新 更多