I have attempted to do Model View Controller in Python for learning. First, I am not sure if dictionaries are faster than lists for about N = 30 (20 to 40) items. Second, how maintainable is this design since I got abstraction but not very much encapsulation? The abstraction comes from abstracting towards to the domain classes.
The classes and their purposes:
- Table Model is a data class in the domain model.
- Total Price Model is an algorithm to compute the total price of Table Models.
- Table File handles File I/O of and sets the fields of Table Model.
- Table View handles user I/O which is currently command line.
main
is a driver program to run the code.
class TableModel:
"""
Data Model of a table
"""
def __init__(self, key :int, name: str, price_cents: int) -> None:
"""
:param key: computer generated number
:param name: name of this what
:param price_cents: price in USD, in cents, of this what
"""
self.key = key
self.name = name
self.price_cents = price_cents
def __str__(self) -> str:
"""
Input: None \n
Output: string to allow for debuging (print object out) \n
"""
string = "key: " + str(self.key) + "\n"
string = string + "name: " + self.name + "\n"
string = string + "price_cents: " + str(self.price_cents) + "\n"
return string
class TotalPriceModel:
"""
Algorithm for total price
"""
def total_price_cents(self, table_models:dict) -> int:
"""
:param table_models: the table models
Output: total price
"""
cents = 0
for key in table_models:
cents = cents + table_models[key].price_cents
return cents
class TableFile:
"""
Models the file of table
Note: this is a text file (for now anyways)
"""
def __init__(self, table_models:[TableModel]) -> None:
"""
:param table_models: dict, keys are str(table_model.key) and val are the talbe_model
"""
self.table_models = table_models
def to_file(self) -> None:
"""
Input: None \n
Output: None \n
Note: puts info into file \n
"""
file = open("save_file.txt", "r+")
name = ""
price_cents = ""
key = ""
for key in range(self.table_models):
name = self.table_models[key].name
price_cents = str(self.table_models[key].price_cents)
key = str(self.table_models[key].key)
file.write(name + "#" + price_cents + "#" + key)
file.close()
def from_file(self) -> None:
"""
Input: None \n
Output: None \n
Note: puts file info into this \n
"""
file = open("save_file.txt", "r+")
name = ""
price_cents = 0
key = 0
table_model = None
#use gen so that the extra space is less
for line in file.readlines():
name = line.split("#")[0]
price_cents = int(line.split("#")[1])
key = int(line.split("#")[2])
table_model = TableModel(key, name, price_cents)
self.table_models.append(table_model)
file.close()
def add(self, what:TableModel) -> None:
"""
:param what: what to add
Output: None
"""
self.table_models[str(what.key)] = what
def remove(self, key:int) -> None:
"""
:param key: key of the object to remove
Output: None \n
Note: if this not in the class, this results in nothing done
"""
if key in self.table_models:
del self.table_models[str(key)]
def change(self, table:TableModel) -> None:
"""
:param table: table that was changed using the computer \n
Output: None \n
"""
self.table_models[str(table.key)] = table
class TableView:
"""
User Input and User Output
"""
def __init__(self, tables_model:dict) -> None:
self.tables = tables_model
def render(self) -> None:
"""
Input: None \n
Output: None \n
Note: outputs to user
"""
print("Table View")
name = ""
price_cents = ""
for key in self.tables:
name = self.tables[key].name
price_cents = str(self.tables[key].price_cents)
print(name + " " + price_cents)
def change_event(self, table: TableModel) -> None:
"""
:param table: table that was changed using the computer
Output: None
"""
self.tables[str(table.key)] = table
def append_event(self, table: TableModel) -> None:
"""
:param table: table that was added within the computer
Output: None
"""
self.tables[str(table.key)] = table
def remove_event(self, table: TableModel) -> None:
"""
:param table: table that was removed from the computer
Output: None
"""
if str(table.key) in self.tables:
del self.tables[str(table.key)]
def main() -> None:
"""
Input: None \n
Output: None \n
Note: Runs the code, can test using std output \n
"""
table_model = TableModel(0, "Bob's Table", 0)
table_view = TableView({})
table_view.append_event(table_model)
table_view.render()
print(table_model)
print("hello world")
if __name__ == "__main__":
main()
1 Answer 1
Overview
You've done an excellent job:
- You did a good job partitioning code into functions
- You used meaningful names for classes, functions and variables
- You added documentation for the functions
- The
main
function makes it easy to see how to run some of the code
Here are some adjustments to consider, mainly for coding style.
Lint check
pylint identified a few style issues.
Consider using with
to open
a file. This has the side benefit
of simplifying the code since there is no need to call close
.
Simpler
You can simplify the code in the __str__
function using the +=
assignment operator:
def __str__(self) -> str:
"""
Input: None
Output: string to allow for debuging (print object out)
"""
string = "key: " + str(self.key) + "\n"
string += "name: " + self.name + "\n"
string += "price_cents: " + str(self.price_cents) + "\n"
return string
Documentation
It is good practice to add a summary of the purpose of the code at top of the file.
Some of the docstrings have a literal \n
, while others do not:
def from_file(self) -> None:
"""
Input: None \n
Output: None \n
Note: puts file info into this \n
"""
If you don't need the \n
for any reason, I recommend removing them.
Add more information for the TableFile
class methods because I could not
figure out how to use them. For example, it seems strange that the
to_file
function uses "r+"
for the file open
mode to open a file for
writing.
f-string
In some instances, using f-strings instead of concatenation may be cleaner. For example, change:
print(name + " " + price_cents)
to:
print(f'{name} {price_cents}')
Typo
"debuging" should be "debugging".
"talbe" should be "table".