2011-10-21 25 views
5

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

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:[email protected]/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)) 
+0

“实例”是什么类型?一个yaml.dump(10)工作正常,所以我可能是一个SQLAlchemy类型,它没有所需的酸洗方法。一个__reduce__方法本身返回可选择的类型)。 –

回答

2

尝试添加以下到您的类:

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

我很困惑,因为print(yaml.dump(fhash))工作正常,所以它不会在那里抱怨。为什么来自查询的类型会改变? – esac

+0

“打印实例”后,放入“打印类型(实例)”。你得到了什么? –

+0

打印'' – esac

1

事实证明,默认reduce_ex方法(即时通讯相当肯定这是一个在对象()中,但不一定是)。当sqlalchemy处于活动状态时,该对象会添加一个_sa_instance_state成员到PyYAML用来执行序列化的API中返回的'状态'。

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

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

这个查询实例链接,据我所知,使用的方法可以让你回溯到在同一个python解释器的生命周期内生成给定对象的查询。

如果你关心那个功能(像session.new & session.dirty之类的东西),你需要在PyYAML的序列化器中实现对它的支持。

如果你不在意,只想要你声明的成员,你可以使用一个基类,它隐藏了从调用到减少*的链接 - 注意,这也将打破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,这将为任何基类生成正确的行为,同时检测是否仅声明了减少()。

Redux ... reduce将返回实例对象的实际字典 - 我们不想从中删除,因此对于__reduce *,我们必须实际上浅拷贝该字典。

相关问题