当对象来自SQLAlchemy时,无法pickle int对象错误?

时间:2011-10-21 02:10:24

标签: python sqlalchemy yaml pickle

我正在使用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))

2 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

事实证明,默认的 reduce_ex 方法(我非常确定这是object()中的那个,但它并不一定是。)当你离开时让sqlalchemy有效,将_sa_instance_state成员添加到&#39;州&#39;在 reduce_ex API中返回,PyYAML用它来执行序列化。

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

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

正如我所理解的,这个查询实例链接用于为方法提供支持,让你回顾一下在同一个python解释器的生命周期内生成给定对象的查询。

如果您关心该功能(例如session.new&amp; session.dirty),您需要在PyYAML的序列化程序中实现对该功能的支持。

如果您不关心,只是想要宣传您的会员,您可以使用隐藏&#39;从调用 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兼容。根据{{​​3}},这将为任何基类产生正确的行为,同时检测是否只声明 reduce ()。

Redux ... reduce将返回实例对象的实际 dict - 我们不想从那里删除,所以对于__reduce *,我们实际上必须浅拷贝那个字典。