2011-10-21 31 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 *,我們必須實際上淺拷貝該字典。

相關問題