2. The Shop class: creating, saving and loading¶
Lines 8–26
Shop is the core of the program and knows nothing about the window. It holds two pieces of data: a dictionary of products and a list of sales. This part covers its first three methods: the constructor, save, which writes everything to disk, and load, which reads it back.
Code for this part¶
8class Shop:9 def __init__(self):10 self.products = {}11 self.sales = []12 self.load()13 14 def save(self):15 with open(FILE, 'w', encoding='utf-8') as f:16 json.dump({'products': self.products, 'sales': self.sales}, f, ensure_ascii=False, indent=2)17 18 def load(self):19 if os.path.exists(FILE):20 try:21 with open(FILE, 'r', encoding='utf-8') as f:22 data = json.load(f)23 self.products = data.get('products', {})24 self.sales = data.get('sales', [])25 except (OSError, json.JSONDecodeError, AttributeError):26 self.products, self.sales = {}, []
Click a line number to jump to its explanation; click “line …” in an explanation to highlight that line in the code.
Line by line¶
- line 8
class Shop:¶ - Defines the
Shopclass, a template that keeps related data and behaviour together. Nothing is created yet; the actual object is built on line 65 withShop(). - line 9
def __init__(self):¶ - The constructor. Python runs it automatically every time
Shop()is called.selfis the object being created, and attributes are attached to it throughself. - line 10
self.products = {}¶ - An empty products dictionary. Each key is a product name and each value is a small dictionary such as
{'buy': 1000, 'sell': 1500, 'qty': 3}(purchase price, sale price, stock). - line 11
self.sales = []¶ - An empty list of sales. Each sale is a dictionary like
{'name': 'pen', 'qty': 2, 'total': 3000}, appended on line 59. - line 12
self.load()¶ - Loads any saved data straight away. The order matters: the empty defaults are created first (line 10 and line 11), so if there is no file the object simply keeps them.
- line 14
def save(self):¶ savewrites all of the object's data to the file. It needs no argument besidesself, because it takes everything from the object.- line 15
with open(FILE, 'w', encoding='utf-8') as f:¶ - Opens the file in write mode (
'w'), which replaces any existing content or creates the file.withguarantees the file is closed at the end, even if an error occurs.encoding='utf-8'keeps non-English product names intact.fis the open file. - line 16
json.dump({'products': self.products, 'sales': self.sales}, f, ensure_ascii=False, indent=2)¶ - Builds a dictionary with two keys,
productsandsales, and writes it tofwithjson.dump.ensure_ascii=Falsestores non-ASCII text as-is instead of escapes such as\u0645, andindent=2indents each level by two spaces so people can read the file too. - line 18
def load(self):¶ loadreads the data from the file and puts it on the object. It is only called from the constructor (line 12).- line 19
if os.path.exists(FILE):¶ - First checks that the file exists. On the very first run there is no file, so the whole block is skipped.
- line 20
try:¶ - Starts a
tryblock: an error in any of the lines below jumps to theexceptclause (line 25) instead of stopping the program. - line 21
with open(FILE, 'r', encoding='utf-8') as f:¶ - Opens the file in read mode (
'r') with the same UTF-8 encoding. - line 22
data = json.load(f)¶ json.loadreads the file and turns its text into Python objects — here a dictionary — stored indata.- line 23
self.products = data.get('products', {})¶ - Takes the value of the
productskey. If the key is missing,getreturns its second argument,{}, instead of raisingKeyError. - line 25
except (OSError, json.JSONDecodeError, AttributeError):¶ - Catches three kinds of error:
OSErrorwhen the file cannot be opened (no permission, for example),json.JSONDecodeErrorwhen it is not valid JSON, andAttributeErrorwhen it is valid JSON but not a dictionary (a list, say), because a list has nogetmethod. The parentheses mean “any of these”.
How it connects to the rest of the code¶
See also
Shop- line 65 creates one instance named
shop, which every user-interface function uses. self.products- Read or changed in
addline 33,editline 41,deleteline 47,sellline 55 and in the table on line 116. self.sales- Grows in
sellline 59 and is summed inreportline 63. save()- Called after every change: line 34, line 42, line 48 and line 60. Its file errors (
OSError) are caught on line 90. load()- Called once, in the constructor line 12.
Check your understanding¶
Try to answer each question before opening it.
What happens the very first time the program runs, when there is no data file?
What does
withon line 15 give you that a plainf = open(...)would not?Show answer
The file is closed automatically when the block ends, even if
json.dumpraises an error.What is the difference between
data.get('products', {})anddata['products']?Show answer
If the key is missing,
getreturns{}, whiledata['products']raisesKeyError.A data file contains
[1, 2]. What happens when the program starts?