JustToThePoint English Website Version
JustToThePoint en español
JustToThePoint in Thai

Pickle, Python object serialization

Introduction

The pickle module implements binary protocols for serializing and de-serializing a Python object structure.

“Pickling” or “serialization” is the process of converting an object to a byte stream that can be stored on a disk or sent over a network. “Unpickling” or “deserialization” is the inverse operation, it is the process of converting a byte stream back into a Python object.

The pickle library

The pickle library

A Hello World example

import pickle

class Person: # We are going to create a simple class named Person with three properties: name, age, and sex.
	def __init__(self, name="Joe", age=18, sex="man"):
		self.name = name
		self.age = age
		self.sex = sex
	
	def save(self): # It saves our object.
		with open('person.pkl', 'wb') as f:
			pickle.dump(self.name, f) # The name is pickled or serialized into a binary file (person.pkl).
			pickle.dump(self.age, f)  # The dump function writes the pickled representation of the name, age, and sex to the file object.
			pickle.dump(self.sex, f)
			
	def restore(self): # It restores our object.
		with open('person.pkl', 'rb') as f:
			self.name = pickle.load(f) # We unpickle or deserialize the name from the same binary file.
			self.age = pickle.load(f) # The load function reads the pickled representation of our object's attributes or properties from the file object and returns them.
			self.sex = pickle.load(f)
			
	def __str__(self):
		return "My name is " + self.name + ". My age is " + str(self.age) + " years old. " + \
		"I am a " + self.sex + "."

	def getting_old(self):
		self.age += 1

if __name__ == '__main__':
	Anne = Person("Anne", 23, "woman")
	Anne.save()
	Anne.getting_old()
	print(Anne)
	Bew = Person()
	print(Bew)
	Bew.restore()
	print(Bew)

My name is Anne. My age is 24 years old. I am a woman. My name is Joe. My age is 18 years old. I am a man. My name is Anne. My age is 23 years old. I am a woman.

A Real-world example

We are going to use Python’s pickle module to allow the user to save and restore his current Connect-4 game session.

import pickle
class Game:
    def __init__(self):
         [...]
         self.movements = [] # We are going to save all the game's movements in a list.
         self.F1_KEY = False # We are going to use the key "F1", too. The user will be able to access the main menu at any given moment and save/restore the current game session. 
    # The method game_loop controls the overall flow for the entire game program. There are three tasks: 1. Check all events. 2. Process these events and take action accordingly. 3. Draw the current state of the game so the player can see what is going on.
    def game_loop(self):
         while self.playing:
            [...]
            if self.MOUSEBUTTONDOWN: # The mouse button is released. The player has already decided his or her next move.
                pygame.draw.rect(self.screen, BLACK, (0, 0, Board.WIDTH, Board.SQUARESIZE))
                if self.player.get_turn():

First, we calculate the column where the user has released the mouse. Second, if the column is a valid movement, we find out which is the row where the piece is to be placed, and finally we save or “drop” the piece into the board and add it to the list of movements (self.movements.append(col)).

                    col = int(math.floor(self.posx/Board.SQUARESIZE))
                    if self.board.is_valid_location(col):
                        row = self.board.get_next_open_row(col)
                        self.movements.append(col)
                        self.board.drop_piece(row, col, PLAYER_PIECE)
            [...]
            # If the turn belongs to the AI player and the game is not over yet. This level of indentation corresponds to "if self.MOUSEBUTTONDOWN:"
            if self.ai.get_turn() and not self.game_over: 
                # We use the board's minimax method to find out the best movement.
                col, minimax_score = self.board.minimax(self.depth, -math.inf, math.inf, True) 
                # If the column is a valid movement, we find out which is the row where the piece is to be placed, save or "drop" the piece into the board, and **add it to the list of movements** (_self.movements.append(col)_).
                if self.board.is_valid_location(col): 
                    row = self.board.get_next_open_row(col) 
                    self.movements.append(col)
                    self.board.drop_piece(row, col, AI_PIECE)

Pickle is used for serializing and de-serializing Python objects. Serialization refers to the process of converting an object to a byte stream that can be stored on a disk or sent over a network. Later on, it can then be retrieved and de-serialized back to a Python object.

class Game:
     [...]
     def save_game(self): # Save our game current session or, more specifically, our list of movements. 
        with open('connect.pkl', 'wb') as f: # The list is pickled or serialized into a binary file (connect.pkl).
            pickle.dump(self.movements, f) # The dump function writes the pickled representation of the list to the file object. 

    def restore_game(self): # Restore a saved game session.
        with open('connect.pkl', 'rb') as f: #  We unpickle or deserialize the list from the same binary file.
            self.movements = pickle.load(f) # The load function reads the pickled representation of our list from the file object and returns the "reconstituted" list.

        self.board = Board(self.screen, self.debug, self.depth) # We initialize our game.
        self.posx = 0
        self.player = Player(PLAYER, True)
        self.ai = Player(AI, False)
        i = 0

        for movement in self.movements: # We iterate over our list of movements and place each piece in its place.
            if i % 2 == 0: 
                row = self.board.get_next_open_row(movement)
                self.board.drop_piece(row, movement, PLAYER_PIECE)
                self.player.set_turn(False)
                self.ai.set_turn(True)
            else:
                row = self.board.get_next_open_row(movement)
                self.board.drop_piece(row, movement, AI_PIECE)
                self.ai.set_turn(False)
                self.player.set_turn(True)
            i += 1

        self.draw()

     def check_events(self): # Pygame handles all its event messaging through an event queue. This method checks all these events. 
        for event in pygame.event.get(): # It iterates over the events of the event queue.
            if event.type == pygame.QUIT:
                self.running, self.playing = False, False
                self.curr_menu.run_display = False
                sys.exit()

            if event.type == pygame.MOUSEMOTION:
                self.MOUSEMOTION = True
                self.posx = event.pos[0]

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RETURN:
                    self.START_KEY = True
                if event.key == pygame.K_BACKSPACE:
                    self.BACK_KEY = True
                if event.key == pygame.K_DOWN:
                    self.DOWN_KEY = True
                if event.key == pygame.K_UP:
                    self.UP_KEY = True
                if event.key == pygame.K_F1:

If the user presses “F1”, we stop playing (self.playing = False), and then, we display the menu.

                    self.F1_KEY = True
                    self.running, self.playing = True, False
                    self.curr_menu.run_display = True

            if event.type == pygame.MOUSEBUTTONDOWN:
                self.MOUSEBUTTONDOWN = True
                self.posx = event.pos[0]

     def reset_keys(self): # Reset all the keys
        self.UP_KEY, self.DOWN_KEY, self.START_KEY, self.BACK_KEY = False, False, False, False
        self.MOUSEBUTTONDOWN, self.F1_KEY = False, False
        self.MOUSEMOTION = False

Our menu presents some small changes:

class MainMenu(Menu):
    def __init__(self, game):
        Menu._init__(self, game)
        self.state = "Start" # A property that indicates the menu's state or the user's selected option
        self.startx, self.starty = self.mid_w, self.mid_h + 30
        self.optionsx, self.optionsy = self.mid_w, self.mid_h + 80
        self.savex, self.savey = self.mid_w, self.mid_h + 130
        self.restorex, self.restorey = self.mid_w, self.mid_h + 180
        self.creditsx, self.creditsy = self.mid_w, self.mid_h + 230
        self.cursor_rect.midtop = (self.startx + self.offset, self.starty)

This is the first screen/menu which appears on the game. It is the initial “state” of our game.

    def display_menu(self):  
        while self.run_display: # self.run.display is set to True by the Menu's (the parent class) constructor.
            self.game.check_events() # We check all the events.
            self.process_events() # And process the events.
            self.game.screen.fill((0, 0, 0)) # We draw the Main Menu.
            self.game.draw_text('Main Menu', 20, self.game.board.WIDTH / 2, self.game.board.HEIGHT / 3 - 20)
            self.game.draw_text("Start Game", 20, self.startx, self.starty)
            self.game.draw_text("Difficulty", 20, self.optionsx, self.optionsy)
            self.game.draw_text("Save Game", 20, self.savex, self.savey)
            self.game.draw_text("Restore Game", 20, self.restorex, self.restorey)
            self.game.draw_text("Credits", 20, self.creditsx, self.creditsy)
            self.draw_cursor()
            self.process_events() # Finally, we update the menu.
We need a menu!

We need a menu!

    def move_cursor(self): # If the user presses the arrows up and/or down, we need to change the "state" property and the cursor's position.
        if self.game.DOWN_KEY:
            if self.state == 'Start':
                self.cursor_rect.midtop = (self.optionsx + self.offset, self.optionsy)
                self.state = 'Difficulty'
            elif self.state == 'Difficulty':
                self.cursor_rect.midtop = (self.savex + self.offset, self.savey)
                self.state = 'Save'
            elif self.state == 'Save':
                self.cursor_rect.midtop = (self.restorex + self.offset, self.restorey)
                self.state = 'Restore'
            elif self.state == 'Restore':
                self.cursor_rect.midtop = (self.creditsx + self.offset, self.creditsy)
                self.state = 'Credits'
            elif self.state == 'Credits':
                self.cursor_rect.midtop = (self.startx + self.offset, self.starty)
                self.state = 'Start'
        elif self.game.UP_KEY:
            if self.state == 'Start':
                self.cursor_rect.midtop = (self.creditsx + self.offset, self.creditsy)
                self.state = 'Credits'
            elif self.state == 'Difficulty':
                self.cursor_rect.midtop = (self.startx + self.offset, self.starty)
                self.state = 'Start'
            elif self.state == 'Save':
                self.cursor_rect.midtop = (self.optionsx + self.offset, self.optionsy)
                self.state = 'Difficulty'
            elif self.state == 'Restore':
                self.cursor_rect.midtop = (self.savex + self.offset, self.savey)
                self.state = 'Save'
            elif self.state == 'Credits':
                self.cursor_rect.midtop = (self.restorex + self.offset, self.restorey)
                self.state = 'Restore'

    def process_events(self):
        self.move_cursor()
        if self.game.START_KEY: # If the user has pressed pygame.K_RETURN, the enter key...
            if self.state == 'Start': # and the "state" is 'Start',
                self.game.playing = True # we set the property "playing" of our instance of the class Game (self.game) to True so he can start playing the game. 
                self.game.MOUSEMOTION = True
                self.game.posx = 0
            elif self.state == 'Save': # If the user has pressed pygame.K_RETURN and the "state" is 'Save',
                self.game.save_game() # we save the game current session.
            elif self.state == 'Restore': # If the user has pressed pygame.K_RETURN and the "state" is 'Restore',
                self.game.playing = True # we restore the game (self.game.restore_game()) and set the property "playing" of our instance of the class Game (self.game) to True so he can start playing the "restored" game 
                self.game.MOUSEMOTION = True
                self.game.posx = 0
                self.game.restore_game()
            elif self.state == 'Difficulty':
                self.game.curr_menu = self.game.difficulty # We change the "curr_menu" property of our instance of the class Game (self.game) to self.game.difficulty, an instance of the class DifficultyMenu.
            elif self.state == 'Credits':
                self.game.curr_menu = self.game.credits # We change the "curr_menu" property of our instance of the class Game (self.game) to self.game.credits, an instance of the class CreditsMenu.
            self.run_display = False # Finally, we update the display property to False.
Bitcoin donation

JustToThePoint Copyright © 2011 - 2024 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.