Hone logo
Hone
Problems

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:

  1. Product Dataclass:

    • 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 sku field is unique for each Product instance.
    • Implement a method within the Product dataclass to calculate the total value of the stock for that product (price * quantity_in_stock).
  2. Inventory Class:

    • Define a regular Python class named Inventory.
    • This class should manage a collection of Product objects. A dictionary, where the SKU is the key and the Product object is the value, is a suitable data structure.
    • Implement the following methods:
      • add_product(product: Product): Adds a Product to 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 the Product object associated with a given SKU. Returns None if the SKU is not found.
      • list_all_products(): Returns a list of all Product objects 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.

Expected Behavior:

  • Instances of Product should be easily created and hold their data.
  • The Product dataclass should automatically generate __init__, __repr__, and __eq__ methods.
  • The Inventory class 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 dataclasses module in Python. The @dataclass decorator is your primary tool.
  • Consider how you will handle the uniqueness of SKUs. The dataclasses module itself doesn't enforce uniqueness across instances; this logic needs to be implemented in your Inventory class.
  • Think about the data structures that would be most efficient for the Inventory class 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 ValueError or KeyError.
Loading editor...
python