Decoupled Model-View-Controller example
diff --git a/mvc.py b/mvc.py
index 8087bdab..7df613fc 100644
--- a/mvc.py
+++ b/mvc.py
@@ -1,60 +1,121 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
-
 class Model(object):
+    def __iter__(self):
+        raise NotImplementedError
+
+    def get(self, item):
+        """Returns an an object with a .items() call method
+        that iterates over key,value pairs of its information."""
+        raise NotImplementedError
+
+    @property
+    def item_type(self):
+        raise NotImplementedError
+
+
+
+class ProductModel(Model):
+
+    class Price(float):
+        """A polymorphic way to pass a float with a particular __str__ functionality."""
+        def __str__(self):
+            first_digits_str = str(round(self,2))
+            try:
+                dot_location = first_digits_str.index('.')
+            except ValueError:
+                return (first_digits_str + '.00')
+            else:
+                return (first_digits_str +
+                               '0'*(3 + dot_location - len(first_digits_str)))
 
     products = {
-        'milk': {'price': 1.50, 'quantity': 10},
-        'eggs': {'price': 0.20, 'quantity': 100},
-        'cheese': {'price': 2.00, 'quantity': 10}
+        'milk': {'price': Price(1.50), 'quantity': 10},
+        'eggs': {'price': Price(0.20), 'quantity': 100},
+        'cheese': {'price': Price(2.00), 'quantity': 10}
     }
 
+    item_type = 'product'
+
+    def __iter__(self):
+        for item in self.products:
+            yield item
+
+    def get(self, product):
+        try:
+            return self.products[product]
+        except KeyError as e:
+            raise KeyError((str(e) + " not in the model's item list."))
 
 class View(object):
+    def show_item_list(self, item_type, item_list):
+        raise NotImplementedError
 
-    def product_list(self, product_list):
-        print('PRODUCT LIST:')
-        for product in product_list:
-            print(product)
-        print('')
+    def show_item_information(self, item_type, item_name, item_info):
+        """Will look for item information by iterating over key,value pairs
+        yielded by item_info.items()"""
+        raise NotImplementedError
 
-    def product_information(self, product, product_info):
-        print('PRODUCT INFORMATION:')
-        print('Name: %s, Price: %.2f, Quantity: %d\n' %
-              (product.title(), product_info.get('price', 0),
-               product_info.get('quantity', 0)))
+    def item_not_found(self, item_type, item_name):
+        raise NotImplementedError
 
-    def product_not_found(self, product):
-        print('That product "%s" does not exist in the records' % product)
+class ConsoleView(View):
 
+    def show_item_list(self, item_type, item_list):
+        print(item_type.upper() + ' LIST:')
+        for item in item_list:
+            print(item)
+        print('')
 
-class Controller(object):
+    @staticmethod
+    def capitalizer(string):
+        return string[0].upper() + string[1:].lower()
 
-    def __init__(self):
-        self.model = Model()
-        self.view = View()
+    def show_item_information(self, item_type, item_name, item_info):
+        print(item_type.upper() + ' INFORMATION:')
+        printout = 'Name: %s' % item_name
+        for key, value in item_info.items():
+            printout += (', ' + self.capitalizer(str(key)) + ': ' + str(value))
+        printout += '\n'
+        print(printout)
 
-    def get_product_list(self):
-        product_list = self.model.products.keys()
-        self.view.product_list(product_list)
+    def item_not_found(self, item_type, item_name):
+        print('That %s "%s" does not exist in the records' % (item_type, item_name))
+
+
+class Controller(object):
 
-    def get_product_information(self, product):
-        product_info = self.model.products.get(product, None)
-        if product_info is not None:
-            self.view.product_information(product, product_info)
+    def __init__(self, model, view):
+        self.model = model
+        self.view = view
+
+    def show_items(self):
+        items = list(self.model)
+        item_type = self.model.item_type
+        self.view.show_item_list(item_type, items)
+
+    def show_item_information(self, item_name):
+        try:
+            item_info = self.model.get(item_name)
+        except:
+            item_type = self.model.item_type
+            self.view.item_not_found(item_type, item_name)
         else:
-            self.view.product_not_found(product)
+            item_type = self.model.item_type
+            self.view.show_item_information(item_type, item_name, item_info)
 
 
 if __name__ == '__main__':
 
-    controller = Controller()
-    controller.get_product_list()
-    controller.get_product_information('cheese')
-    controller.get_product_information('eggs')
-    controller.get_product_information('milk')
-    controller.get_product_information('arepas')
+    model = ProductModel()
+    view = ConsoleView()
+    controller = Controller(model, view)
+    controller.show_items()
+    controller.show_item_information('cheese')
+    controller.show_item_information('eggs')
+    controller.show_item_information('milk')
+    controller.show_item_information('arepas')
 
 ### OUTPUT ###
 # PRODUCT LIST:

Decoupled the Controller from the Model and the View. Controllers, models, and views only know about their abstract interfaces. 

Controller now takes a concrete implementation of a model and a view during initialization. Besides the base Model and View classes that guarantee their services, for this example there is a ProductModel and a ConsoleView to serve as their concrete implementations.
