Each request might be served from a completely different process, on a different server, which might even be on a separate datacenter (hey, maybe in a different continent). There is nothing that's guaranteed to be "globally accessible" to the handlers of different requests to the same app except the datastore (even memcache's entries might disappear at any time if things get too busy: it's a cache, after all!-).
Perhaps you can keep your "static tree structure" in a data file that you upload together with the application's code, and access it from disk in lieu of "initializing".
Edit: as requested, here's a rough and ready example of the "lightweight class mapping tree into array" approach which I mentioned in a comment -- not tuned nor finely tested. I'm taking as the example a binary search tree with integer payloads and assuming that for some reason it's important to keep the exact structure in the "light" tree as in the "heavy" tree it represents. Even with these simplifications it's still a lot of code, but, here comes:
import array
import random
def _doinsert(tree, payload):
if tree is None: return HeavyTree(payload)
tree.insert(payload)
return tree
class HeavyTree(object):
def __init__(self, payload):
self.payload = payload
self.left = self.right = None
def insert(self, other):
if other <= self.payload:
self.left = _doinsert(self.left, other)
else:
self.right = _doinsert(self.right, other)
def walk(self):
if self.left:
for x in self.left.walk(): yield x
yield self.payload
if self.right:
for x in self.right.walk(): yield x
def walknodes(self):
yield self
if self.left:
for x in self.left.walknodes(): yield x
if self.right:
for x in self.right.walknodes(): yield x
data = [random.randint(0, 99) for _ in range(9)]
print 'data: ',
for x in data: print x,
print
theiter = iter(data)
thetree = HeavyTree(next(theiter))
for x in theiter: thetree.insert(x)
print
print 'Heavy tree:'
print 'nodes:',
for x in thetree.walknodes(): print x.payload,
print
print 'inord:',
for x in thetree.walk(): print x,
print
class LightTree(HeavyTree):
def __init__(self, base, offset):
self.base = base
self.offset = offset
@property
def payload(self):
return self.base[self.offset]
@property
def left(self):
return self._astree(self.offset+1)
@property
def right(self):
return self._astree(self.offset+2)
def _astree(self, i):
offset = self.base[i]
if offset < 0: return None
return LightTree(self.base, offset)
def heavy_to_light(heavy):
for i, node in enumerate(heavy.walknodes()):
node.id = i * 3
base = array.array('l', (i+1) * 3 * [-1])
for node in heavy.walknodes():
base[node.id] = node.payload
if node.left: base[node.id+1] = node.left.id
if node.right: base[node.id+2] = node.right.id
return LightTree(base, 0)
print
print 'Light tree:'
light = heavy_to_light(thetree)
print 'nodes:',
for x in light.walknodes(): print x.payload,
print
print 'base :',
for x in light.base: print x,
print
print 'inord:',
for x in light.walk(): print x,
print
A typical run would show:
data: 27 79 90 60 82 80 3 94 76
Heavy tree:
nodes: 27 3 79 60 76 90 82 80 94
inord: 3 27 60 76 79 80 82 90 94
Light tree:
nodes: 27 3 79 60 76 90 82 80 94
base : 27 3 6 3 -1 -1 79 9 15 60 -1 12 76 -1 -1 90 18 24 82 21 -1 80 -1 -1 94 -1 -1
inord: 3 27 60 76 79 80 82 90 94
variable in details every time, of course, since the data are being generated randomly.
Maybe this sort of thing is just too cumbersome for anybody who didn't start with good old Fortran (and thus inevitably learned how to represent logical pointers as indices into an array), as I did back in EE school many decades ago;-). But loading such arrays from a file straight into memory is blazingly fast (compared to unpickling and the like)...!-)