In order to structure data, it makes sense to use hierarchical schemes, as for example filesystems structure files in nested directories. This recipe offers an easy to use class which shows an analog behavior as the 'super mkdir' os.makedirs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | #!/usr/bin/env python
class HierarchicalData(object):
"""
organizes hierarchical data as a tree.
for convenience inner nodes need not be constructed
explicitly. see examples below.
"""
def __init__(self):
# self._d stores subtrees
self._d = {}
def __getattr__(self, name):
# only attributes not starting with "_" are organinzed
# in the tree
if not name.startswith("_"):
return self._d.setdefault(name, HierarchicalData())
raise AttributeError("object %r has no attribute %s" % (self, name))
def __getstate__(self):
# for pickling
return self._d, self._attributes()
def __setstate__(self, tp):
# for unpickling
d,l = tp
self._d = d
for name,obj in l: setattr(self, name, obj)
def _attributes(self):
# return 'leaves' of the data tree
return [(s, getattr(self, s)) for s in dir(self) if not s.startswith("_") ]
def _getLeaves(self, prefix=""):
# getLeaves tree, starting with self
# prefix stores name of tree node above
prefix = prefix and prefix + "."
rv = {}
atl = self._d.keys()
for at in atl:
ob = getattr(self, at)
trv = ob._getLeaves(prefix+at)
rv.update(trv)
for at, ob in self._attributes():
rv[prefix+at] = ob
return rv
def __str__(self):
# easy to read string representation of data
rl = []
for k,v in self._getLeaves().items():
rl.append("%s = %s" % (k,v))
return "\n".join(rl)
def getLeaves(ob, pre=""):
""" getLeavess tree, returns dictionary mapping
paths from root to leafs to value of leafs
"""
return ob._getLeaves(pre)
if __name__=="__main__":
model=HierarchicalData()
# model.person is contstruted on the fly:
model.person.surname = "uwe"
model.person.name = "schmitt"
model.number = 1
print
print "access via attributes:"
print
print "model.person.surname=", model.person.surname
print "model.person.name=", model.person.name
print "model.number=", model.number
print
print "print complete model:"
print
print model
print
import pickle
o = pickle.loads(pickle.dumps(model))
print "unpickle after pickle:"
print
print o
print
print "paths from root to leaves and values at leaves:"
print
print getLeaves(o)
|
This class works in the same way as the 'super mkdir' os.makedir: internal nodes of the structure (subdirectorys in the case of os.makedir) are constructed automatically if needed. So in the example above 'model.person.surname' raises no exception although model.person was not constructed before.
I developed this class as an implemenation of the 'model' part of an MVC pattern. It supports pickling / unpickling.
typos. The "leaves" should be "leafs" ...