Python-Ref > Object oriented programming > Real world example > Cookbook II.
 
 

<-^^

Cookbook II.

The following code builds on the one shown in the previous example. It adds support for Ingredients, adds method to Cookbook to add new Receipts (instead of having to use the low-level access to the recipes attribute) and also uses some general code for Receipt dom creation based on getattr function.
Expand/Shrink
<cookbook>
  <recipe>
    <title>Cheese cake</title>
    <text>Here comes the description for preparation of a cheese cake.</text>
    <ingredient amount="500g">soft cheese</ingredient>
    <ingredient amount="2">eggs</ingredient>
    <ingredient amount="100g">sugar</ingredient>
    <ingredient>raisins</ingredient>
  </recipe>
  <recipe>
    <title>Rosted duck</title>
    <text>Here we describe how to prepare a roasted duck.</text>
    <ingredient amount="1">duck</ingredient>
    <ingredient>cumin</ingredient>
    <ingredient amount="1">apple</ingredient>
  </recipe>
</cookbook>
Zdroj: (cookbook2-1.py)
  1   import sys
  2   import xml.dom.minidom as dom
  3   
  4   class Recipe( object):
  5   
  6     def __init__( self, title="", text=""):
  7       self.title = title
  8       self.text = text
  9       self.ingredients = []
 10   
 11     def __str__( self):
 12       return "Recipe: %s (%d ingredients)" % (self.title, len( self.ingredients))
 13   
 14     def read_dom_element( self, e):
 15       titles = e.getElementsByTagName( "title")
 16       if not titles:
 17         raise ValueError( "Could not find title in the dom element")
 18       elif len( titles) > 1:
 19         print >> sys.stderr, "Warning: more than one title in dom element\n", titles
 20       self.title = get_all_text_from_element( titles[0])
 21       texts = e.getElementsByTagName( "text")
 22       if texts:
 23         self.text = get_all_text_from_element( texts[0])
 24       for ie in e.getElementsByTagName( "ingredient"):
 25         i = Ingredient()
 26         i.read_dom_element( ie)
 27         self.add_ingredient( i)
 28   
 29     def get_dom_element( self, doc):
 30       el = doc.createElement( "recipe")
 31       for attr_name in ['title','text']:
 32         attr_el = doc.createElement( attr_name)
 33         attr_el.appendChild( doc.createTextNode( getattr( self, attr_name)))
 34         el.appendChild( attr_el)
 35       for i in self.ingredients:
 36         ie = i.get_dom_element( doc)
 37         el.appendChild( ie)
 38       return el
 39   
 40     def add_ingredient( self, i):
 41       self.ingredients.append( i)
 42   
 43   
 44   class Ingredient( object):
 45   
 46     def __init__( self, name="", amount=""):
 47       self.name = name
 48       self.amount = amount
 49   
 50     def __str__( self):
 51       if self.amount:
 52         return "%s %s" % (self.amount, self.title)
 53       else:
 54         return "%s" % self.title
 55   
 56     def read_dom_element( self, e):
 57       if e.hasAttribute( "amount"):
 58         self.amount = e.getAttribute( "amount")
 59       self.name = get_all_text_from_element( e)
 60   
 61     def get_dom_element( self, doc):
 62       el = doc.createElement( "ingredient")
 63       if self.amount:
 64         el.setAttribute( "amount", str( self.amount))
 65       el.appendChild( doc.createTextNode( self.name))
 66       return el
 67   
 68   
 69   
 70   class CookBook( object):
 71   
 72     def __init__( self):
 73       self.recipes = []
 74   
 75     def __str__( self):
 76       return "Cookbook: %d recipes" % len( self.recipes)
 77   
 78     def read_xml_file( self, filename):
 79       doc = dom.parse( filename)
 80       for el in doc.getElementsByTagName( "recipe"):
 81         rec = Recipe()
 82         rec.read_dom_element( el)
 83         self.recipes.append( rec)
 84   
 85     def write_xml_file( self, filename):
 86       doc = dom.Document()
 87       root = doc.createElement( "cookbook")
 88       doc.appendChild( root)
 89       for rec in self.recipes:
 90         e = rec.get_dom_element( doc)
 91         root.appendChild( e)
 92       f = file( filename, "w")
 93       f.write( doc.toxml())
 94   
 95     def add_recipe( self, recipe):
 96       self.recipes.append( recipe)
 97   
 98   # help functions
 99   
100   def get_all_text_from_element( el):
101       text = ""
102       for ch in el.childNodes:
103           if isinstance( ch, dom.Element):
104               text += get_all_text_from_element( ch)
105           if isinstance( ch, dom.Text):
106               text += ch.data
107       return text
108       
109   # end of help functions
110   
111   
112   if __name__ == "__main__":
113     c = CookBook()
114     c.read_xml_file( "infiles/cookbook2.xml")
115     print c
116     for rec in c.recipes:
117       print rec
118     new = Recipe( "Potato dumplings")
119     new.text = "Preparation instructions for czech potato dumplings"
120     c.add_recipe( new)
121     new.add_ingredient( Ingredient( name="potatoes", amount="500g"))
122     c.write_xml_file( "cookbook2-2.xml")
123     print c
stdout:
Cookbook: 2 recipes
Recipe: Cheese cake (4 ingredients)
Recipe: Rosted duck (3 ingredients)
Cookbook: 3 recipes
<?xml version="1.0" ?>
<cookbook><recipe><title>Cheese cake</title><text>Here comes the description for preparation of a cheese cake.</text><ingredient amount="500g">soft cheese</ingredient><ingredient amount="2">eggs</ingredient><ingredient amount="100g">sugar</ingredient><ingredient>raisins</ingredient></recipe><recipe><title>Rosted duck</title><text>Here we describe how to prepare a roasted duck.</text><ingredient amount="1">duck</ingredient><ingredient>cumin</ingredient><ingredient amount="1">apple</ingredient></recipe><recipe><title>Potato dumplings</title><text>Preparation instructions for czech potato dumplings</text><ingredient amount="500g">potatoes</ingredient></recipe></cookbook>
Doba běhu: 29.8 ms
The next incarnation of the cookbook includes just one minor, but very useful, change - the names of dom elements are not hardcoded as string, but they are taken from the class attribute "dom_element_name" of corresponding classes. This makes for cleaner and more easily managable code.
Expand/Shrink
<cookbook>
  <recipe>
    <title>Cheese cake</title>
    <text>Here comes the description for preparation of a cheese cake.</text>
    <ingredient amount="500g">soft cheese</ingredient>
    <ingredient amount="2">eggs</ingredient>
    <ingredient amount="100g">sugar</ingredient>
    <ingredient>raisins</ingredient>
  </recipe>
  <recipe>
    <title>Rosted duck</title>
    <text>Here we describe how to prepare a roasted duck.</text>
    <ingredient amount="1">duck</ingredient>
    <ingredient>cumin</ingredient>
    <ingredient amount="1">apple</ingredient>
  </recipe>
</cookbook>
Rozdíl proti: cookbook2-1.py
@@ -1,7 +1,9 @@
-import sys
+
 import xml.dom.minidom as dom
 class Recipe( object):
+
+  dom_element_name = "recipe"
   def __init__( self, title="", text=""):
     self.title = title
@@ -21,13 +23,13 @@
     texts = e.getElementsByTagName( "text")
     if texts:
       self.text = get_all_text_from_element( texts[0])
-    for ie in e.getElementsByTagName( "ingredient"):
+    for ie in e.getElementsByTagName( Ingredient.dom_element_name):
       i = Ingredient()
       i.read_dom_element( ie)
       self.add_ingredient( i)
   def get_dom_element( self, doc):
-    el = doc.createElement( "recipe")
+    el = doc.createElement( self.dom_element_name)
     for attr_name in ['title','text']:
       attr_el = doc.createElement( attr_name)
       attr_el.appendChild( doc.createTextNode( getattr( self, attr_name)))
@@ -42,6 +44,8 @@
 class Ingredient( object):
+
+  dom_element_name = "ingredient"
   def __init__( self, name="", amount=""):
     self.name = name
@@ -59,7 +63,7 @@
     self.name = get_all_text_from_element( e)
   def get_dom_element( self, doc):
-    el = doc.createElement( "ingredient")
+    el = doc.createElement( self.dom_element_name)
     if self.amount:
       el.setAttribute( "amount", str( self.amount))
     el.appendChild( doc.createTextNode( self.name))
@@ -77,7 +81,7 @@
   def read_xml_file( self, filename):
     doc = dom.parse( filename)
-    for el in doc.getElementsByTagName( "recipe"):
+    for el in doc.getElementsByTagName( Recipe.dom_element_name):
       rec = Recipe()
       rec.read_dom_element( el)
       self.recipes.append( rec)
stdout:
Cookbook: 2 recipes
Recipe: Cheese cake (4 ingredients)
Recipe: Rosted duck (3 ingredients)
Cookbook: 3 recipes
<?xml version="1.0" ?>
<cookbook><recipe><title>Cheese cake</title><text>Here comes the description for preparation of a cheese cake.</text><ingredient amount="500g">soft cheese</ingredient><ingredient amount="2">eggs</ingredient><ingredient amount="100g">sugar</ingredient><ingredient>raisins</ingredient></recipe><recipe><title>Rosted duck</title><text>Here we describe how to prepare a roasted duck.</text><ingredient amount="1">duck</ingredient><ingredient>cumin</ingredient><ingredient amount="1">apple</ingredient></recipe><recipe><title>Potato dumplings</title><text>Preparation instructions for czech potato dumplings</text><ingredient amount="500g">potatoes</ingredient></recipe></cookbook>
Doba běhu: 30.0 ms
This OOP example continues in the chapter on GTK GUI - Real world example.