How to store the reference of a dictionary element ?

Bengt Richter bokr at oz.net
Fri Dec 20 17:05:18 EST 2002


On 19 Dec 2002 13:58:22 GMT, bokr at oz.net (Bengt Richter) wrote:

>On 19 Dec 2002 12:29:49 +0800, "Alfredo P. Ricafort" <alpot at mylinuxsite.com> wrote:
>
>>On Wed, 2002-12-18 at 06:36, Bengt Richter wrote:
>>
[...]
>====< menutree.py >==============================================================
>#!/usr/bin/python
># menutree.py for "Alfredo P. Ricafort" <alpot at mylinuxsite.com>
>
>inputData=(
>    ('/&File',               'textF0','textF1','F2'),
>    ('/File/&New',           'textFN0','textFN1'),
>    ('/File/New/&Folder',    'textFNF0','textFNF1'),
>    ('/File/New/&Directory', 'textFND0','textFND1','textFND2'),
>    ('/&Edit',               'textE0','textE1')
>)
>
>class Node:
>    def __init__(self, name, parent=None, attrTup=()):
>        __slots__ = ['name','disp','parent', 'children','attrTup']    # optional, saves space
The __slots__ line should not have been in this file. It accidentally did no harm here because
1. it was local to __init__ instead of being a class variable
2. Node did not derive from object.

If it had been inserted correctly, it would have made adding a new attribute dynamically
to a node illegal, as a hack in the test code did, by adding .treeDict. And which I also
did in an interactive log. Instead the logical thing would have been to use the
attrTup slot (maybe in a tuple for consistency with the name or anything else that might
want to see a tuple container there, but not otherwise necessarily).

the updated code is:
====< menutree.py >==============================================================
#!/usr/bin/python
# menutree.py for "Alfredo P. Ricafort" <alpot at mylinuxsite.com>
# 2002-12-20 bokr at oz.net corrected __slots__ usage and the then illegal attr addition in test code
#
inputData=(
    ('/&File',               'textF0','textF1','F2'),
    ('/File/&New',           'textFN0','textFN1'),
    ('/File/New/&Folder',    'textFNF0','textFNF1'),
    ('/File/New/&Directory', 'textFND0','textFND1','textFND2'),
    ('/&Edit',               'textE0','textE1')
)

class Node(object):
    __slots__ = ['name','disp','parent', 'children','attrTup']    # optional, saves space
    def __init__(self, name, parent=None, attrTup=()):
        self.name = name.replace('&','') # without shortcut &
        self.disp = name                 # menu display text
        self.parent = parent
        self.children = []
        self.attrTup = attrTup

    def addChild(self, child):
        if not isinstance(child, Node):
            raise ValueError, 'Children must be Node instances, not %s' % type(child)
        child.parent = self             # the parent link you wanted ;-)
        self.children.append(child)

    def getChild(self, name):
        for child in self.children:
            if child.name == name:
                return child
        raise ValueError, 'Node "%s" has no child named "%s"' % (self.name, name)

    def show(self, recurse=0, level=0):
        print '%s %-30s"%s" %s' %(level*4*' ', self.name+':', self.disp, self.attrTup)
        if recurse:
            for child in self.children: child.show(recurse,level+1)

    def __repr__(self): return '<Node %s at 0x%08x>' %( `self.name`, id(self))
        
    def makeDict(tree):
        treed = {}
        def addNodeNameKey(node):
            if treed.get(node.name): raise ValueError, 'Non-Unique Node name: %s' % node.name
            treed[node.name]=node
            for child in node.children: addNodeNameKey(child)
        addNodeNameKey(tree)
        return treed
    makeDict = staticmethod(makeDict)
    
    def makeTree(name, inputData):
        tree = Node(name)
        for tup in inputData:
            path = tup[0].split('/')[1:] # assumes path starts with '/' and no insignificant spaces
            currNode = tree
            for name in path:
                try:
                    currNode = currNode.getChild(name.replace('&',''))
                except ValueError:
                    child = Node(name, currNode, tup[1:])   # currNode is parent
                    currNode.addChild(child)
                    currNode = child
        return tree
    makeTree = staticmethod(makeTree)   # so it can be called without a node instance
                
def test(inData):
    menuTree = Node.makeTree('Menu Bar',inData)  # give root node a name
    menuTree.show(1,0)
    menuTree.attrTup = (Node.makeDict(menuTree),) # attach optional directory for test
    return menuTree # so we can print dir showing access via root node

inputData2=(    # test out of order and reorderd input
    ('/File/New/&Directory', 'textFND0','textFND1','textFND2'),
    ('/File/New/&Folder',    'textFNF0','textFNF1'),
    ('/&File',               'textF0','textF1','F2'),
    ('/File/&New',           'textFN0','textFN1'),
    ('/&Edit',               'textE0','textE1'),
    ('/File/E&xit',          'TTFN ;-)')
)

if __name__ == '__main__':
    def boxit(s):
        print ' +-%s-+' % ('-'*len(s)) 
        print ' | %s |' % s
        print ' +-%s-+' % ('-'*len(s)) 
    boxit('From ordered data')
    root = test(inputData)
    boxit('Directory of all nodenames (must be unique) to look up named nodes')
    print '{\n%s}' % (''.join([('%s:%s,\n' % (`k`,`v`)) for k,v in root.attrTup[0].items()]))
    boxit('Parent links of nodes in dict')
    for k,v in root.attrTup[0].items(): print '%15s is parent of %s' % (
        v.parent and v.parent.name or '(No parent)', k)
    boxit('From out of order & reordered input with added item')
    test(inputData2)
=================================================================================
[ test output is the same ]

The part of the interaction that wouldn't have worked started with:

 >>> mt2 = menutree.Node.makeTree('Test',menutree.inputData2)
 >>> mt2.treeDict = menutree.Node.makeDict(mt2) # attach dict to root
        ^^^^^^^^^--not legal now without 'treeDict' in the __slots__ list

As we'll show:

 >>> import menutree
 >>> mt2 = menutree.Node.makeTree('Test',menutree.inputData2)
 >>> mt2.treeDict = menutree.Node.makeDict(mt2) # attach dict to root
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 AttributeError: 'Node' object has no attribute 'treeDict'

Instead we'll put it in the attrTup slot, and then walk around a bit again:

 >>> mt2.attrTup = (menutree.Node.makeDict(mt2),) # put dict in root attrTup in tuple
 >>> mt2
 <Node 'Test' at 0x007d9fd0>
 >>> mt2.attrTup[0].keys()
 ['Directory', 'Exit', 'File', 'Test', 'New', 'Folder', 'Edit']
 >>> mt2.attrTup[0]['Exit']
 <Node 'Exit' at 0x007d8a90>
 >>> mt2.attrTup[0]['Exit'].parent
 <Node 'File' at 0x007d8f40>
 >>> mt2.attrTup[0]['Exit'].parent.parent
 <Node 'Test' at 0x007d9fd0>
 >>> mt2.attrTup[0]['Exit'].parent.parent.getChild('File')
 <Node 'File' at 0x007d8f40>
 >>> mt2.attrTup[0]['Exit'].parent.parent.getChild('File').show(1)
  File:                         "File" ('textFND0', 'textFND1', 'textFND2')
      New:                          "New" ('textFND0', 'textFND1', 'textFND2')
          Directory:                    "&Directory" ('textFND0', 'textFND1', 'textFND2')
          Folder:                       "&Folder" ('textFNF0', 'textFNF1')
      Exit:                         "E&xit" ('TTFN ;-)',)
 >>> mt2.attrTup[0]['Exit'].parent.parent.getChild('File').getChild('New').children[1]
 <Node 'Folder' at 0x007d9d20>
 >>> mt2.attrTup[0]['Exit'].parent.parent.getChild('File').getChild('New').children[1].disp
 '&Folder' 

And so forth...

 >>> mt2.attrTup[0]['Exit'].attrTup[0]
 'TTFN ;-)'

Sorry for any confusion caused ;-/

Regards,
Bengt Richter



More information about the Python-list mailing list