Welcome, guest | Sign In | My Account | Store | Cart

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.

Python, 97 lines
 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.

1 comment

Uwe Schmitt (author) 19 years, 10 months ago  # | flag

typos. The "leaves" should be "leafs" ...

Created by Uwe Schmitt on Fri, 18 Jun 2004 (PSF)
Python recipes (4591)
Uwe Schmitt's recipes (4)
Rapid prototyping (11)

Required Modules

Other Information and Tasks