How to store the reference of a dictionary element ?

Bengt Richter bokr at oz.net
Sat Dec 21 16:57:11 EST 2002


On 21 Dec 2002 10:31:29 +0800, "Alfredo P. Ricafort" <alpot at mylinuxsite.com> wrote:

>Hi,
>
>First of all, thanks to everyone who replied, especially for those
>people who took time to write codes.
>
>Using the ideas and suggestions of others, I think of a solution that
>fits my requirement.  The idea is to use handler, or **ptr(pointer to
>pointer). So in Python, I used List as my handler. 
>
>Here's the code:
>
># this is my custom list
>class mylist(list):
>      def __setitem__(self,k,v):
>          self[k]=v
Is there a reason you are using a list as opposed to something you could
access more human-readably? (plus I guess you have other methods, or
you could just use a standard list, since you don't seem to be making
use of the distinction) See [1.1] and [1.2]

>
># this is my custom dict
>class mydict(dict):
>      def __setitem__(self,k,v): 
>          if self.has_key(k):            
>             item=self[k]
>             item.remove(item[0])
              item.pop(0) # should do the same
>             item.append(v)
              # the net effect would seem to use the list like a fifo except
              # that I don't see _this_ list having any length but 1, so

              # for what you seem to be doing, you could just write
              self[k][0]=v   # for this entire if branch
>          else:
>             item=mylist() 
>             item.append(v)
              item=mylist(v) # would probably do the same as the above two
              # and if you have no other need for a separate list subtype,

              # you can simplify to
              item = [v]  # for this entire if branch

           # and eliminate this one:
>          if isinstance(v,mylist):
>             item=v
              # [1.1] so you want either to fifo the list or replace it?
              # I didn't find an example of using this branch of code. Did you intend to at [1.2]?
              # possibly you could eliminate it.
>          dict.__setitem__(self,k,item)
>
What you show here could be done with a standard dict and list. I'll insert examples using
dMenu instead of aMenu...

>
>For example, if the menu structure is like this:
>
>aMenu={'key':[label,help,ptrToParent]} 
>
>then storing data is simply like this:
>
>#  Storing 3 records, with the 2 records pointing to 1 parent
>>>> aMenu=mydict()
     bMenu={}
'key':[[label,help,ptrToParent]]}
>>>> aMenu['/File']=['&File','File Help',None]         <---- parent
     bMenu['/File']=[['&File','File Help',None]]

>>>> aMenu['/File/New']=['&New','New Help', aMenu['/File']]
Correctness of the parent link depends on your writing it correctly if you do it this way.
     bMenu['/File/New']=[['&New','New Help', bMenu['/File']]]

>>>> aMenu['/File/Open']=['&Open','Open Help',aMenu['/File']]
     bMenu['/File/Open']=[['&Open','Open Help',bMenu['/File']]]
>
># The 2 children points to the parent correctly
because you wrote aMenu['/File'] correctly ;-)

>>>> aMenu['/File/New']
>[['&New', 'New Help', [['&File', 'File Help', None]]]]
 >>> bMenu['/File/New']
 [['&New', 'New Help', [['&File', 'File Help', None]]]]

>>>
>>>> aMenu['/File/Open']
>[['&Open', 'Open Help', [['&File', 'File Help', None]]]]
 >>> bMenu['/File/Open']
 [['&Open', 'Open Help', [['&File', 'File Help', None]]]]
>
># Changing the 'help' description of the parent by replacing the whole
>record. Children can still see the change
because they are referring to the same container, which you manipulate
using remove and append

>>>> aMenu['/File']=['&File','Help on File',None]
[1.2] This doesn't change the dict item, because it's a list, not
an instance of myList, so you do remove/append replacing the content
I.e., you didn't make use of [1.1] which you do with plain dict and lists like:
 >>> bMenu['/File'][0]=['&File','Help on File',None]

>>>> aMenu['/File/New']
>[['&New', 'New Help', [['&File', 'Help on File', None]]]]
 >>> bMenu['/File/New']
 [['&New', 'New Help', [['&File', 'Help on File', None]]]]

>>>> aMenu['/File/Open']
>[['&Open', 'Open Help', [['&File', 'Help on File', None]]]]
 >>> bMenu['/File/Open']
 [['&Open', 'Open Help', [['&File', 'Help on File', None]]]]
>
># unlinking one of the children
I'd call it unlinking the parent by setting the child's child->parent
link to None
>>>> aMenu['/File/New'][0][2]=None
     aMenu['/File/New'].parent=None
would read more clearly ISTM, but this is exactly the same for bMenu
 >>> bMenu['/File/New'][0][2]=None

>>>> aMenu['/File/New']
>[['&New', 'New Help', None]]
 >>> bMenu['/File/New']
 [['&New', 'New Help', None]]

>
>>>> aMenu['/File/Open']
>[['&Open', 'Open Help', [['&File', 'Help on File', None]]]]
 >>> bMenu['/File/Open']
 [['&Open', 'Open Help', [['&File', 'Help on File', None]]]]

>
># changing the 'help' description of the parent. The remaining child
>still sees the changes.
>>>> aMenu['/File'][0][1]='Back to File Help'
wouldn't it be nicer to write
     aMenu['/File'].help = 'Back to File Help'
anyway, this is the same for bMenu
 >>> bMenu['/File'][0][1]='Back to File Help'

>>>> aMenu['/File/Open']
>[['&Open', 'Open Help', [['&File', 'Back to File Help', None]]]] 
 >>> bMenu['/File/Open']
 [['&Open', 'Open Help', [['&File', 'Back to File Help', None]]]]
>
>>>> aMenu['/File/New']
>[['&New', 'New Help', None]]
 >>> bMenu['/File/New']
 [['&New', 'New Help', None]]
>
>
>But this solution suffers from the need to subscript everything with [0]
>to get to the items. I still finding ways to overcome that.
>
As you see, you really didn't make use of the subclassing very much.
It just eliminated an extra enclosing [] in a few statements, because
your dict __setitem__ method effectively supplied them. But it really
didn't do anything more.

I am wondering what you use the parent link for. Anyway, to sum up,
if you want to use your dict class as above, you can simplify it and
use a plain list as the value container:

class mydict(dict):
    def __setitem__(self,k,v):
        # add this this for automatic parent linking
        # assuming k is of form '/xxx/yyy/parent/this' and parent '/xxx/yyy/parent'
        v=v[:]; v[2] = self.get('/'.join(k.split('/')[:-1])) # this line for parent link
        if self.has_key(k):            
            self[k][0]=v
        else:
            dict.__setitem__(self,k,[v])

If no parent key exists, the slot gets a None. If you want the parent link
back in the caller's list, leave out the "v=v[:];"

 >>> cMenu = mydict()
 >>> cMenu['/File']=['&File','File Help',None]
 >>> cMenu['/File']
 [['&File', 'File Help', None]]
 >>> cMenu['/File/Open']=['&Open','Open Help',None]
 >>> cMenu['/File/Open']
 [['&Open', 'Open Help', [['&File', 'File Help', None]]]]
 KeyError: /File/New
 >>> cMenu['/File/New']=['&New','New Help', cMenu['/File']]
 >>> cMenu['/File/New']
 [['&New', 'New Help', [['&File', 'File Help', None]]]]

Slot 2 gets automatically replaced by a link to parent, though I don't
know why it's needed with a full dictionary of all paths since if
your looking up item k with aMenu[k] you can look up the parent like

 >>> def parent(k): return '/'.join(k.split('/')[:-1])
 ...
 >>> cMenu['/File/New']
 [['&New', 'New Help', [['&File', 'File Help', None]]]]

Now the same thing and parent:
 >>> key = '/File/New'
 >>> cMenu[key]
 [['&New', 'New Help', [['&File', 'File Help', None]]]]
 >>> cMenu.get(parent(key))
 [['&File', 'File Help', None]]
 >>>

So you might consider eliminating the link, since it can be
derived from the the keys you are using and a .get()

I guess my other post didn't give you many ideas, but you might
consider using a stripped down Node from it to replace what you are
accomplishing with a list above. E.g.,

class Node(object):
    """aMenu={'key':[label,help,ptrToParent]}"""
    __slots__ = ['label','help','ptrToParent']
    def __init__(self, label, help='', ptrToParent=None):
        self.label = label
        self.help = help
        self.ptrToParent = ptrToParent
    # give it representation
    def __repr__(self): return '<Node %s >' % self.asList().__repr__()
    def asList(self): return [self.label, self.help, self.ptrToParent]

and a corresponding minor mod to the menu class:

class myDict(dict):
    def __setitem__(self,k,v):
        # add this this for automatic parent linking
        # assuming k is of form '/xxx/yyy/parent/this' and parent '/xxx/yyy/parent'
        v.ptrToParent =  self.get('/'.join(k.split('/')[:-1])) # this line for parent link
        if self.has_key(k):
            oldv = self[k]
            oldv.label = v.label; oldv.help=v.help; oldv.ptrToParent = v.ptrToParent            
            self[k]=oldv   # keep old node so changes will be seen through refs to old node
        else:
            dict.__setitem__(self,k,v)

Of course this is only if you need the parent link in the nodes, which you don't if you have
full path keys and the parent function above. Then you could just use a plain dict with the
Node class above. But first the above, doing the operations you were showing:

 >>> dMenu = myDict()
 >>> dMenu['/File'] = Node('&File','File Help')
 >>> dMenu['/File']
 <Node ['&File', 'File Help', None] >
 >>> dMenu['/File/Open'] = Node('&Open','Open Help', None)
 >>> dMenu['/File/Open']
 <Node ['&Open', 'Open Help', <Node ['&File', 'File Help', None] >] >
 >>> dMenu['/File/New'] = Node('&New','New Help', None)
 >>> dMenu['/File/New']
 <Node ['&New', 'New Help', <Node ['&File', 'File Help', None] >] >

Unlink
 >>> dMenu['/File/New'].ptrToParent=None
 >>> dMenu['/File/New']
 <Node ['&New', 'New Help', None] >
 >>> dMenu['/File/Open']
 <Node ['&Open', 'Open Help', <Node ['&File', 'File Help', None] >] >

Change help
 >>> dMenu['/File'].help = 'Back to File Help' # back??
 >>> dMenu['/File/Open']
 <Node ['&Open', 'Open Help', <Node ['&File', 'Back to File Help', None] >] >
 >>> dMenu['/File/New']
 <Node ['&New', 'New Help', None] >

Note that the displayed result is from "<Node " + representation of node as list + " >"
E.g.,

The node repr
 >>> dMenu['/File/New']
 <Node ['&New', 'New Help', None] >

contains
 >>> dMenu['/File'].asList()
 ['&File', 'Back to File Help', None]

and we killed the parent link above, so
 >>> dMenu['/File/New'].asList()
 ['&New', 'New Help', None]

but it is intact in
 >>> dMenu['/File/Open'].asList()
 ['&Open', 'Open Help', <Node ['&File', 'Back to File Help', None] >]

so instead of None there is a parent link, and you get the repr of the parent appearing
as the last element in the repr of the list. The first two elements in someNode.asList()
are the same as in your list usage, so you can retrieve them that way if you prefer, i.e.,
some equivalent accesses:

 >>> dMenu['/File/Open'].asList()[0]
 '&Open'
 >>> dMenu['/File/Open'].label
 '&Open'

 >>> dMenu['/File/Open'].asList()[1]
 'Open Help'
 >>> dMenu['/File/Open'].help
 'Open Help'

 >>> dMenu['/File/Open'].asList()[2]
 <Node ['&File', 'Back to File Help', None] >
 >>> dMenu['/File/Open'].ptrToParent
 <Node ['&File', 'Back to File Help', None] >

 >>> dMenu['/File/Open'].asList()[2].label
 '&File'
 >>> dMenu['/File/Open'].ptrToParent.label
 '&File'

I'll leave it to you to eliminate the special dict and auto parent linking if
you want to go the parent(key) function route. It would be a simple thing, and
it would simplify the node __repr__ also. It could be pretty lean. But I'm done.
Happy Holdidays.

Regards,
Bengt Richter



More information about the Python-list mailing list