【问题标题】:Can't pickle int object error when object comes from SQLAlchemy?当对象来自 SQLAlchemy 时不能腌制 int 对象错误?
【发布时间】:2011-10-21 02:10:24
【问题描述】:

我正在使用 YAML 和 SQLAlchemy。我定义了我的对象,我可以使用 YAML 来打印它就好了。但是,当我尝试对从 SQLAlchemy 查询返回的对象使用 YAML 时,它失败并出现错误 can't pickle int objects。我打印出从 SQLAlchemy 返回的实例,它显示了正确的类型。我让代码来说话:

class HashPointer(Base):
    __tablename__ = 'hash_pointers'

    id = Column(Integer, primary_key=True)
    hash_code = Column(VARBINARY(64), unique=True)
    file_pointer = Column(Text)

    def __init__(self, hash_code, file_pointer):
        self.hash_code = hash_code
        self.file_pointer = file_pointer

    def __repr__(self):
        return "<HashPointer('%s', '%s')>" % (self.hash_code, self.file_pointer)

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Engine = create_engine("mysql://user:pass@localhost/db", echo=True)
Session = sessionmaker(bind=Engine)
session = Session()
fhash = HashPointer(0x661623708235, "c:\\test\\001.txt")

# PRINTS FINE
print(yaml.dump(fhash))

for instance in session.query(HashPointer).all():
    # PRINTS FINE AS __repr__
    print instance

    # THROWS ERROR, 'CAN'T PICKLE INT OBJECTS'
    print(yaml.dump(instance))

【问题讨论】:

  • “实例”的类型是什么? yaml.dump(10) 工作正常,所以我可能是没有所需酸洗方法的 SQLAlchemy 类型(即本身返回可酸洗类型的 reduce 方法)。跨度>

标签: python sqlalchemy yaml pickle


【解决方案1】:

尝试将以下内容添加到您的课程中:

def __reduce__(self):
    'Return state information for pickling'
    return self.__class__, (int(self.hash_code), str(self.file_pointer))

【讨论】:

  • 我很困惑,因为 print(yaml.dump(fhash)) 工作正常,所以它不会在那里抱怨。为什么来自查询时类型会改变?
  • 在“打印实例”之后,加上“打印类型(实例)”。你得到了什么?
  • 打印 ''
  • 啊,那就加上上图的__reduce__()方法。它教 YAML 如何腌制一个 HashPointer,而不需要不必要地在不可腌制的 SQLAlchemy 部分中啜饮。
  • 有道理。只是不确定为什么 fhash 返回的类型与对 SQLalchemy 的查询返回的类型不完全相同。但至少我现在知道如何解决它:)
【解决方案2】:

事实证明,当您使用 sqlalchemy 时,默认的 reduce_ex 方法(我很确定这是 object() 中的方法,但不一定是。)活动,将_sa_instance_state 成员添加到 PyYAML 用于执行序列化的 reduce_ex API 中返回的“状态”。

当序列化来自 SqlAlchemy 查询的对象时,这本质上是对象元数据的隐藏部分,可供进一步操作访问。

PyYAML 序列化程序失败的正是这个对象。您可以通过在 PDB 中运行序列化并在调用堆栈中查看对代表对象的 两次 调用来验证这一点,即使对于相对简单的 SQLAlchemy 查询对象结果也是如此。

据我了解,此查询实例链接用于为方法提供动力,让您可以回顾从同一 python 解释器的生命周期内生成给定对象的查询。

如果您关心该功能(诸如 session.new 和 session.dirty 之类的东西),您将需要在 PyYAML 的序列化程序中实现对它的支持。

如果你不在乎,只想要你声明的成员,你可以使用一个基类来“隐藏”从调用到 reduce* 的链接——请注意,这也会破坏SQLAlchemy 序列化程序扩展也是如此,因此请仔细考虑您的计划。

实现该更改的基类示例如下:

DeclBase = declarative_base()

class Base(DeclBase):
    __abstract__ = True

    def __reduce_ex__(self, proto):
        ret = super(Base, self).__reduce_ex__(proto)
        ret = ( ret[0], ret[1], dict(ret[2]) ) + ret[3:]
        ret[2].pop('_sa_instance_state', None) # remove bad yamly from reduce state
        return ret

这将允许您将对象往返传入/传出 yaml,尽管往返会将它们与任何待处理的事务或查询解除关联。例如,如果您使用延迟加载的成员,这也可能会产生交互。确保您正在序列化您期望的所有内容。

注意/编辑: 我在这里选择使用 reduce_ex,以与可能的其他基类或 mixin 兼容。根据https://docs.python.org/2/library/pickle.html#object.reduce_ex,这将为任何基类产生正确的行为,同时检测是否仅声明了 reduce()。

Redux...reduce 将返回实例对象的实际 dict ——我们不想从那里删除,所以对于 __reduce*,我们必须实际浅复制该 dict。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-27
    • 2018-10-30
    • 2022-07-16
    • 2021-08-18
    • 2020-06-26
    • 1970-01-01
    • 2018-09-06
    • 2019-12-14
    相关资源
    最近更新 更多