Mastering Python Dataclasses: Building a Simple Inventory System
Python's dataclasses module offers a convenient way to create classes that primarily store data. This challenge will guide you through building a basic inventory system using dataclasses, reinforcing your understanding of their structure, features, and benefits. Mastering dataclasses will streamline your code and make it more readable and maintainable for data-centric applications.
Problem Description
Your task is to create a system for managing inventory items using Python dataclasses. You will define a Product dataclass to represent individual items in the inventory. This dataclass should store information such as the product's name, unique identifier (SKU), price, and current stock quantity. You'll then create an Inventory class that utilizes Product objects to manage a collection of items.
Key Requirements:
-
ProductDataclass:- Define a dataclass named
Product. - It should have the following fields with appropriate type hints:
name(string): The name of the product.sku(string): A unique Stock Keeping Unit identifier.price(float): The price of the product.quantity_in_stock(integer): The current number of items available.
- Ensure the
skufield is unique for eachProductinstance. - Implement a method within the
Productdataclass to calculate the total value of the stock for that product (price * quantity_in_stock).
- Define a dataclass named
-
InventoryClass:- Define a regular Python class named
Inventory. - This class should manage a collection of
Productobjects. A dictionary, where the SKU is the key and theProductobject is the value, is a suitable data structure. - Implement the following methods:
add_product(product: Product): Adds aProductto the inventory. If a product with the same SKU already exists, it should raise an error or update the existing product's quantity.remove_product(sku: str): Removes a product from the inventory given its SKU. If the SKU doesn't exist, it should handle the error gracefully.get_product_by_sku(sku: str): Returns theProductobject associated with a given SKU. ReturnsNoneif the SKU is not found.list_all_products(): Returns a list of allProductobjects currently in the inventory, sorted alphabetically by product name.get_total_inventory_value(): Calculates and returns the total value of all products in the inventory.
- Define a regular Python class named
Expected Behavior:
- Instances of
Productshould be easily created and hold their data. - The
Productdataclass should automatically generate__init__,__repr__, and__eq__methods. - The
Inventoryclass should correctly add, remove, and retrieve products based on their SKUs. - Operations on non-existent SKUs should be handled without crashing the program.
Edge Cases:
- Adding a product with an SKU that already exists.
- Removing a product with an SKU that does not exist.
- Retrieving a product with an SKU that does not exist.
- An empty inventory.
Examples
Example 1: Basic Product Creation and Inventory Management
from dataclasses import dataclass, field
# Assume Product and Inventory classes are defined as per the description
# Create Product instances
laptop = Product(name="Laptop", sku="LAP101", price=1200.50, quantity_in_stock=15)
keyboard = Product(name="Keyboard", sku="KEY202", price=75.00, quantity_in_stock=50)
mouse = Product(name="Mouse", sku="MOU303", price=25.99, quantity_in_stock=100)
# Create an Inventory instance
inventory = Inventory()
# Add products to inventory
inventory.add_product(laptop)
inventory.add_product(keyboard)
inventory.add_product(mouse)
# Get a product by SKU
retrieved_keyboard = inventory.get_product_by_sku("KEY202")
print(f"Retrieved: {retrieved_keyboard}")
# Calculate product stock value
print(f"Total value of {laptop.name} stock: ${laptop.calculate_stock_value():.2f}")
# List all products (should be sorted by name)
all_items = inventory.list_all_products()
print("All products in inventory:")
for item in all_items:
print(item)
Output 1:
Retrieved: Product(name='Keyboard', sku='KEY202', price=75.0, quantity_in_stock=50)
Total value of Laptop stock: $18007.50
All products in inventory:
Product(name='Keyboard', sku='KEY202', price=75.0, quantity_in_stock=50)
Product(name='Laptop', sku='LAP101', price=1200.5, quantity_in_stock=15)
Product(name='Mouse', sku='MOU303', price=25.99, quantity_in_stock=100)
Explanation 1:
We create three Product objects, add them to the Inventory, retrieve one, calculate its stock value, and then list all products, which are automatically sorted by name.
Example 2: Handling Duplicates and Removal
# Assume Product and Inventory classes are defined as per the description
inventory = Inventory()
monitor = Product(name="Monitor", sku="MON404", price=300.00, quantity_in_stock=5)
inventory.add_product(monitor)
# Attempt to add a product with an existing SKU
try:
duplicate_monitor = Product(name="Gaming Monitor", sku="MON404", price=350.00, quantity_in_stock=2)
inventory.add_product(duplicate_monitor) # This should ideally update or raise an error
except ValueError as e:
print(f"Error adding duplicate: {e}")
# Remove a product
inventory.remove_product("MON404")
# Attempt to remove a non-existent product
try:
inventory.remove_product("XYZ999")
except KeyError as e:
print(f"Error removing non-existent: {e}")
print(f"Inventory after operations: {inventory.list_all_products()}")
Output 2:
Error adding duplicate: Product with SKU MON404 already exists.
Error removing non-existent: SKU XYZ999 not found in inventory.
Inventory after operations: []
Explanation 2:
The attempt to add a product with an existing SKU triggers an error (or update, depending on implementation choice). Removing an existing product succeeds, while attempting to remove a non-existent SKU results in a KeyError.
Example 3: Total Inventory Value
# Assume Product and Inventory classes are defined as per the description
inventory = Inventory()
inventory.add_product(Product(name="Tablet", sku="TAB505", price=400.00, quantity_in_stock=10))
inventory.add_product(Product(name="Charger", sku="CHG606", price=20.00, quantity_in_stock=25))
total_value = inventory.get_total_inventory_value()
print(f"Total inventory value: ${total_value:.2f}")
Output 3:
Total inventory value: $4500.00
Explanation 3:
The get_total_inventory_value method correctly sums up the stock value of all products in the inventory.
Constraints
- Product names will be strings.
- SKUs will be unique strings.
- Prices will be non-negative floating-point numbers.
- Quantities in stock will be non-negative integers.
- The number of unique SKUs in the inventory will not exceed 1000.
- The total number of products added to the inventory will not exceed 10000.
- Your solution should be efficient for the given constraints, with operations like add, remove, and get by SKU ideally performing in O(1) or O(log n) time complexity if using data structures like dictionaries or balanced trees.
Notes
- Familiarize yourself with the
dataclassesmodule in Python. The@dataclassdecorator is your primary tool. - Consider how you will handle the uniqueness of SKUs. The
dataclassesmodule itself doesn't enforce uniqueness across instances; this logic needs to be implemented in yourInventoryclass. - Think about the data structures that would be most efficient for the
Inventoryclass to store and retrieve products by SKU. - Remember to include type hints for better code clarity and maintainability.
- For error handling, consider using appropriate exceptions like
ValueErrororKeyError.