Presumendo tu stia studiando python e OOP ti riporto qualche suggerimento sulla tua prima soluzione:
- Gia' che sei agli inizi perdi la brutta abitudine di:
- scrivere codice meta' in italiano e meta' in inglese.
- non tipizzare il codice o tipizzarlo parzialmente.
- non commentare il codice
- creare funzioni lunghe chilometriche non single purpose
- non utilizzare costanti per accedere alle chiavi di un dizionario
- Se stai lavorando con le classi perche' non definire anche una classe menu e InventoryItem? In generale OOP e' molto piu' che un class ed un __init__
- Cerca di separare i feedback all'utente dalla logica delle classi ma piu' in generale. In questo caso la tua funzione start ( il menu ) e' il layer di contatto tra inventario e utente. Rendi evidente questa separazione.
Al contrario di quanto proposto fino ad ora, ovvero utilizzare sqlite, ti propongo una soluzione diversa basata su JSON che ti evita di dover lavorare con query ed sql che trovo fuori contesto per imparare OOP con l'esempio da te riportato.
Utilizzare il caso dell'inventario con sql lo trovo piu' funzionale per apprendere SQL che OOP di python.
Caso diverso e molto didattico, e' quello di implementare un semplice ORM rudimentale.
Passando alla soluzione:
Una semplice rappresentazione del prodotto in inventario:
Python:
from typing import Dict
from inventory_exceptions import InvalidInventoryItemQuantity
class InventoryItem:
def __init__(self, code: int, name: str, quantity: int):
self.code: int = code
self.name: str = name
self.quantity: int = quantity
def update_quantity(self, quantity: int):
self.quantity += quantity
if self.quantity < 0:
self.quantity -= quantity
raise InvalidInventoryItemQuantity
def to_dict(self) -> Dict:
return {"code": self.code, "name": self.name, "quantity": self.quantity}
def __repr__(self) -> str:
return f"ID: {self.code}\t\tNome: {self.name}\t\tQuantità: {self.quantity}"
Il nostro inventario
Python:
import json
from typing import Dict, Optional
from inventory_item import InventoryItem
from inventory_exceptions import EmptyInventory, InventoryItemNotFound, InventoryItemDuplicatedEntry
class InventoryJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, InventoryItem):
return obj.to_dict()
return super().default(obj)
class Inventory:
INVENTORY_FILE = "inventory.json"
def __init__(self):
self._inventory: Dict[int, InventoryItem] = {}
self._load_inventory_from_file()
def _load_inventory_from_file(self):
try:
with open(self.INVENTORY_FILE, "r") as inventory_file:
json_inventory = json.load(inventory_file)
except FileNotFoundError:
print(f"Impossibile caricare l'inventario dal file: {self.INVENTORY_FILE} ")
return
self._inventory = {}
item_value: Dict
for item_key, item_value in json_inventory.items():
self._inventory[int(item_key)] = InventoryItem(*item_value.values())
def save_inventory(self):
with open(self.INVENTORY_FILE, "w") as inventory_file:
json.dump(self._inventory, inventory_file, cls=InventoryJsonEncoder)
def item_exists(self, item_id: int) -> bool:
return item_id in self._inventory
def get_item(self, item_id: int) -> Optional[InventoryItem]:
return self._inventory.get(item_id)
def add_item(self, item_id: int, item_name: str, item_quantity: int):
if self.item_exists(item_id):
raise InventoryItemDuplicatedEntry
new_item = InventoryItem(item_id, item_name, item_quantity)
self._inventory[item_id] = new_item
def remove_item(self, item_id: int) -> InventoryItem:
try:
return self._inventory.pop(item_id)
except KeyError:
raise InventoryItemNotFound
def update_item_quantity(self, item_id: int, quantity: int):
try:
self._inventory[item_id].update_quantity(quantity)
except KeyError:
raise InventoryItemNotFound
def show_inventory_items(self):
if not self._inventory:
raise EmptyInventory
for item in self._inventory.values():
print(item)
Ci sono semplice custom Exceptions che vengono sollevate sia dall inventario che dagli oggetti in inventario
Python:
class InvalidInventoryItemQuantity(Exception):
pass
class InventoryItemNotFound(Exception):
pass
class InventoryItemDuplicatedEntry(Exception):
pass
class EmptyInventory(Exception):
pass
Ed infine il menu
Python:
import sys
from typing import Dict
from inventory import Inventory
from inventory_exceptions import (
EmptyInventory, InventoryItemNotFound, InvalidInventoryItemQuantity, InventoryItemDuplicatedEntry
)
class Menu:
def __init__(self):
self.inventory: Inventory = Inventory()
self.menu_options: Dict = {
"0": self.option_exit_menu,
"1": self.option_show_inventory_items,
"2": self.option_add_item_to_inventory,
"3": self.option_update_item_in_inventory,
"4": self.option_delete_item_from_inventory
}
def option_exit_menu(self):
self.inventory.save_inventory()
sys.exit()
def option_show_inventory_items(self):
try:
self.inventory.show_inventory_items()
except EmptyInventory:
print("Inventario vuoto!")
def option_add_item_to_inventory(self):
item_id, item_name, item_quantity = (
int(input('Inserisci codice prodotto: ')),
str(input('Inserisci nome prodotto: ')),
int(input('Inserisci la quantità: '))
)
if item_id < 0:
raise ValueError
try:
self.inventory.add_item(item_id, item_name, item_quantity)
except InventoryItemDuplicatedEntry:
print('Prodotto già registrato!\n')
print(self.inventory.get_item(item_id))
def option_delete_item_from_inventory(self):
item_id = int(input('Inserisci codice prodotto: '))
try:
removed_item = self.inventory.remove_item(item_id)
print(f"({removed_item.code}, {removed_item.name}) è stato rimosso correttamente!")
except InventoryItemNotFound:
print('Prodotto non trovato!')
def option_update_item_in_inventory(self):
item_id, item_quantity = (int(input('Inserisci codice prodotto: ')), int(input('Inserisci la quantità: ')))
try:
self.inventory.update_item_quantity(item_id, item_quantity)
except InventoryItemNotFound:
print('Prodotto non trovato!')
except InvalidInventoryItemQuantity:
print('Quantità non sufficente!')
print(f"Quantità disponibile: {self.inventory.get_item(item_id).quantity}")
def start_menu_loop(self):
while True:
print("\n\t\t****** MAGAZZINO ******\n\n")
print("""
1) Visualizza tutti i prodotti
2) Aggiungi prodotto
3) Modifica prodotto
4) Elimina prodotto
0) Salva ed Esci
""")
selection: str = input('> ')
try:
callable_menu_function = self.menu_options[selection]
try:
callable_menu_function()
except ValueError:
print('Valore non valido!')
except KeyError:
print("Selezione Invalida")
if __name__ == "__main__":
menu = Menu()
menu.start_menu_loop()
Note sulla soluzione.
Soluzione compatibile con python >= 3.8
Mi scuso per non aver inserito commenti ma typing e naming dovrebbero essere sufficienti a comprendere.
Se utilizzi una versione di python > 3.8 puoi utilizzare le primitive per il Dict
E' stata aggiunta una piccola classe per fare l'encoding degli oggetti InventoryItem
JSON non supporta int come tipi per le chiavi, quindi la rilettura deve castare il giusto tipo.
L'inventario funziona sulla base del dict in memoria e sincronizza i dati con il file e dal file all'avvio e alla chiusura dell'inventario.