2011-10-21 38 views
5

Tôi đang sử dụng YAML và SQLAlchemy. Tôi đã định nghĩa đối tượng của mình, và tôi có thể sử dụng YAML để in mà thôi. Tuy nhiên, khi tôi cố gắng sử dụng YAML trên đối tượng được trả về từ truy vấn SQLAlchemy, nó không thành công với lỗi can't pickle int objects. Tôi đã in ra thể hiện được trả về từ SQLAlchemy, và nó hiển thị đúng loại. Tôi sẽ cho các mã làm như nói chuyện:Không thể chọn lỗi đối tượng int khi đối tượng đến từ 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:[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

Loại "cá thể" là gì? Một yaml.dump (10) hoạt động tốt, vì vậy tôi có thể là một kiểu SQLAlchemy không có phương thức tẩy yêu cầu (tức làphương thức __reduce__ tự trả về các loại có thể chọn). –

Trả lời

2

Hãy thử thêm dòng sau vào lớp học của bạn:

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

Tôi đang bối rối vì bản in (yaml.dump (fhash)) hoạt động tốt, vì vậy nó không phàn nàn ở đó. Tại sao loại thay đổi khi đến từ một truy vấn? – esac

+0

Sau "print instance", đặt "print type (instance)". Bạn được những gì? –

+0

print '' – esac

1

Nó chỉ ra rằng mặc định reduce_ex phương pháp (im khá chắc chắn đây là một trong những trong đối tượng(), nhưng không nhất thiết phải.) xuống dòng khi bạn có sqlalchemy đang hoạt động, hãy thêm thành viên _sa_instance_state vào 'trạng thái' được trả về trong API reduce_ex mà PyYAML sử dụng để thực hiện tuần tự hóa.

Khi tuần tự hóa một đối tượng đến từ truy vấn SqlAlchemy, đây thực chất là một phần ẩn của siêu dữ liệu của đối tượng, có thể truy cập vào các hoạt động tiếp theo.

Đây là đối tượng trong đó trình tự nối tiếp PyYAML không thực hiện được. Bạn có thể xác minh điều này bằng cách chạy serialization của bạn trong PDB, và nhìn thấy hai cuộc gọi để represent_object trong ngăn xếp cuộc gọi của bạn, ngay cả đối với kết quả đối tượng truy vấn SQLAlchemy tương đối đơn giản.

Liên kết cá thể truy vấn này được sử dụng, như tôi hiểu, để cho phép bạn ping lại truy vấn tạo ra một đối tượng đã cho từ bên trong cùng thời gian của trình thông dịch python.

Nếu bạn quan tâm đến chức năng đó (chẳng hạn như session.new & session.dirty), bạn sẽ cần triển khai hỗ trợ cho điều đó trong bộ nối tiếp của PyYAML.

Nếu bạn không quan tâm, và chỉ muốn các thành viên được khai báo, bạn có thể sử dụng lớp cơ sở 'ẩn' liên kết từ các cuộc gọi đến giảm * - lưu ý rằng điều này cũng sẽ làm gián đoạn phần mở rộng SQLAlchemy mặc dù vậy, vì vậy hãy cân nhắc kỹ các kế hoạch của bạn.

Một ví dụ về một lớp cơ sở để thực hiện sự thay đổi đó là:

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 

này sau đó sẽ cho phép bạn đón tới đối tượng của bạn vào/ra của YAML, mặc dù một chuyến đi vòng sẽ tách chúng từ bất kỳ giao dịch chờ giải quyết hoặc truy vấn. Điều này cũng có thể có tương tác nếu bạn đang sử dụng các thành viên lười biếng tải, cho một ví dụ. Đảm bảo bạn đang sắp xếp mọi thứ bạn mong đợi.

LƯU Ý/CHỈNH SỬA: Tôi chọn sử dụng reduce_ex tại đây, để tương thích với các lớp cơ sở hoặc hỗn hợp có thể khác. Theo https://docs.python.org/2/library/pickle.html#object.reduce_ex, điều này sẽ tạo ra hành vi đúng cho bất kỳ lớp cơ sở nào, cũng phát hiện nếu chỉ giảm() đã được khai báo.

Redux ... reduce sẽ trả về dict thực tế của đối tượng mẫu - chúng tôi không muốn xóa từ đó, vì vậy đối với __reduce *, chúng ta phải thực sự nông bản sao đó.

Các vấn đề liên quan