3
\$\begingroup\$

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()
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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".

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.