mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 18:28:49 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			87 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			87 lines
		
	
	
	
		
			2.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Simple example presenting how persistent ID can be used to pickle
 | |
| # external objects by reference.
 | |
| 
 | |
| import pickle
 | |
| import sqlite3
 | |
| from collections import namedtuple
 | |
| 
 | |
| # Simple class representing a record in our database.
 | |
| MemoRecord = namedtuple("MemoRecord", "key, task")
 | |
| 
 | |
| class DBPickler(pickle.Pickler):
 | |
| 
 | |
|     def persistent_id(self, obj):
 | |
|         # Instead of pickling MemoRecord as a regular class instance, we emit a
 | |
|         # persistent ID.
 | |
|         if isinstance(obj, MemoRecord):
 | |
|             # Here, our persistent ID is simply a tuple, containing a tag and a
 | |
|             # key, which refers to a specific record in the database.
 | |
|             return ("MemoRecord", obj.key)
 | |
|         else:
 | |
|             # If obj does not have a persistent ID, return None. This means obj
 | |
|             # needs to be pickled as usual.
 | |
|             return None
 | |
| 
 | |
| 
 | |
| class DBUnpickler(pickle.Unpickler):
 | |
| 
 | |
|     def __init__(self, file, connection):
 | |
|         super().__init__(file)
 | |
|         self.connection = connection
 | |
| 
 | |
|     def persistent_load(self, pid):
 | |
|         # This method is invoked whenever a persistent ID is encountered.
 | |
|         # Here, pid is the tuple returned by DBPickler.
 | |
|         cursor = self.connection.cursor()
 | |
|         type_tag, key_id = pid
 | |
|         if type_tag == "MemoRecord":
 | |
|             # Fetch the referenced record from the database and return it.
 | |
|             cursor.execute("SELECT * FROM memos WHERE key=?", (str(key_id),))
 | |
|             key, task = cursor.fetchone()
 | |
|             return MemoRecord(key, task)
 | |
|         else:
 | |
|             # Always raises an error if you cannot return the correct object.
 | |
|             # Otherwise, the unpickler will think None is the object referenced
 | |
|             # by the persistent ID.
 | |
|             raise pickle.UnpicklingError("unsupported persistent object")
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     import io
 | |
|     import pprint
 | |
| 
 | |
|     # Initialize and populate our database.
 | |
|     conn = sqlite3.connect(":memory:")
 | |
|     cursor = conn.cursor()
 | |
|     cursor.execute("CREATE TABLE memos(key INTEGER PRIMARY KEY, task TEXT)")
 | |
|     tasks = (
 | |
|         'give food to fish',
 | |
|         'prepare group meeting',
 | |
|         'fight with a zebra',
 | |
|         )
 | |
|     for task in tasks:
 | |
|         cursor.execute("INSERT INTO memos VALUES(NULL, ?)", (task,))
 | |
| 
 | |
|     # Fetch the records to be pickled.
 | |
|     cursor.execute("SELECT * FROM memos")
 | |
|     memos = [MemoRecord(key, task) for key, task in cursor]
 | |
|     # Save the records using our custom DBPickler.
 | |
|     file = io.BytesIO()
 | |
|     DBPickler(file).dump(memos)
 | |
| 
 | |
|     print("Pickled records:")
 | |
|     pprint.pprint(memos)
 | |
| 
 | |
|     # Update a record, just for good measure.
 | |
|     cursor.execute("UPDATE memos SET task='learn italian' WHERE key=1")
 | |
| 
 | |
|     # Load the records from the pickle data stream.
 | |
|     file.seek(0)
 | |
|     memos = DBUnpickler(file, conn).load()
 | |
| 
 | |
|     print("Unpickled records:")
 | |
|     pprint.pprint(memos)
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 | 
