Computer Science 111 Fundamentals of Programming I Persistent Data Models Object Serialization
from bank import Bank def createBank(numAccounts = 1): """Returns a new bank with the given number of accounts.""" bank = Bank() upperPin = numAccounts for pinNumber in range(1000, upperPin): bank.add(SavingsAccount('Ken', str(pinNumber))) return bank Helper Function to Create a Bank bank = createBank(5)
Transience and Persistence Data in computer memory are transient and are lost when the program quits Data become persistent when they can be saved to a file and reloaded from a file File storage Data model save load
Saving a Savings Account def save(self, fileObj): # In SavingsAccount fileObj.write(self.name + '\n') fileObj.write(self.pin + '\n') fileObj.write(str(self.balance) + '\n') Include a method for writing an account ’ s info to a text file: >>> a = SavingsAccount('Ken', '8809', ) >>> fileObj = open('account.txt', 'w') >>> a.save(fileObj) >>> fileObj.close() Run the method in the shell and then inspect the file:
Saving an Entire Bank def save(self, fileName): # In Bank fileObj = open(fileName, 'w') for account in self.accounts.values(): account.save(fileObj) fileObj.close() Include a method for writing an bank ’ s info to a text file: >>> bank = createBank(5) >>> bank.save('bank.txt') Run the method in the shell and then inspect the file:
Class Methods vs Instance Methods class MyClass: def instanceMethod(self): return 'I am an instance def classMethod(cls): return 'I am a class method.' Define a class method and an instance method: >>> print(MyClass.classMethod()) I am a class method. >>> obj = MyClass() >>> print(obj.instanceMethod()) I am an instance method. Try both methods in the shell:
Class Methods vs Instance Methods Like a class variable, a class method is used with the class ’ s name An instance method is always used with an instance Class methods can see class variables but not instance variables Instance methods can see both kinds of variables
Why Use Class Methods? Class methods are often used to create and return instances of those classes Such methods are also called factory methods >>> class def getInstance(cls): return MyClass() >>> obj = MyClass.getInstance() Example:
Loading a Savings def load(cls, fileObj): # In SavingsAccount name = fileObj.readline().strip() pin = fileObj.readline().strip() balance = fileObj.readline().strip() if not name: return None else: return SavingsAccount(name, pin, float(balance)) Include a class method to read an account ’ s info from a text file and return a new instance with that info: >>> fileObj = open('account.txt', 'r') >>> a = SavingsAccount.load(fileObj) >>> print(a) Run the method in the shell and then inspect the account:
Loading an Entire def load(cls, fileName): # In Bank fileObj = open(fileName, 'r') bank = Bank() while True: a = SavingsAccount.load(fileObj) if not a: break else: bank.addAccount(a) fileObj.close() return bank Include a class method to load accounts and create a bank: >>> bank = Bank.load('bank.txt') >>> print(bank) Test the method in the shell and then inspect the account:
Problems Lots of manual labor to output and input the attributes of accounts as text Must change this code every time we modify the structure of an account (add an address attribute, etc.) A text file is insecure and inefficient
Solution: Pickle the Data Pickling automates the process of converting an object to a form suitable for file storage Unpickling automates the process of converting data from file storage back to the appropriate objects File storage Data model pickling unpickling
The pickle Module dump(obj, fileObj) # Pickles obj and writes it to fileObj load(fileObj) # Reads an object from fileObj, # unpickles it, and returns it # Raises an exception if the end of # the file is reached
Using pickle for Output >>> import pickle >>> fileObj = open('test.dat', 'wb') >>> pickle.dump('Hi there!', fileObj) >>> fileobj.close() Must use the argument “wb” or “rb” when opening a file for output or input The “b” stands for “byte”
Using pickle for Input >>> import pickle >>> fileObj = open('test.dat', 'wb') >>> pickle.dump('Hi there!', fileObj) >>> fileobj.close() >>> fileObj = open('test.dat', 'rb') >>> s = pickle.load(fileObj) >>> print(s) Hi there!
Using pickle with Accounts >>> import pickle >>> fileObj = open('test.dat', 'wb') >>> account = SavingsAccount('Ken', '8809', ) >>> pickle.dump(account, fileObj) >>> fileobj.close() >>> fileObj = open('test.dat', 'rb') >>> account = pickle.load(fileObj) >>> print(account) Blah blah blah
Saving an Entire Bank (Text) def save(self, fileName): # In Bank fileObj = open(fileName, 'w') for account in self.accounts.values(): account.save(fileObj) fileObj.close() Include a method for writing an bank ’ s info to a text file: >>> bank = createBank(5) >>> bank.save('bank.txt') Run the method in the shell and then inspect the file:
Saving an Entire Bank (Pickled) def save(self, fileName): # In Bank fileObj = open(fileName, 'wb') for account in self.accounts.values(): pickle.dump(account, fileObj) fileObj.close() Include a method for writing an bank ’ s info to a text file: >>> bank = createBank(5) >>> bank.save('bank.dat') Run the method in the shell and then inspect the file:
Loading an Entire Bank def load(cls, fileName): # In Bank fileObj = open(fileName, 'r') bank = Bank() while True: a = SavingsAccount.load(fileObj)) if not a: break else: bank.addAccount(a) fileObj.close() return bank Include a class method to load accounts and create a bank: >>> bank = Bank.load('bank.txt') >>> print(bank) Test the method in the shell and then inspect the account:
Loading an Entire Bank def load(cls, fileName): # In Bank fileObj = open(fileName, 'rb') bank = Bank() try: while True: a = pickle.load(fileObj) bank.addAccount(a) except Exception(e): fileObj.close() return bank Include a class method to load accounts and create a bank: >>> bank = Bank.load('bank.dat') >>> print(bank) Test the method in the shell and then inspect the account: