What do I do when I need a self referential dictionary?

Viewed 23299

I'm new to Python, and am sort of surprised I cannot do this.

dictionary = {
    'a' : '123',
    'b' : dictionary['a'] + '456'
}

I'm wondering what the Pythonic way to correctly do this in my script, because I feel like I'm not the only one that has tried to do this.

EDIT: Enough people were wondering what I'm doing with this, so here are more details for my use cases. Lets say I want to keep dictionary objects to hold file system paths. The paths are relative to other values in the dictionary. For example, this is what one of my dictionaries may look like.

dictionary = {
    'user': 'sholsapp',
    'home': '/home/' + dictionary['user']
}

It is important that at any point in time I may change dictionary['user'] and have all of the dictionaries values reflect the change. Again, this is an example of what I'm using it for, so I hope that it conveys my goal.

From my own research I think I will need to implement a class to do this.

10 Answers

This is very easy in a lazily evaluated language (haskell).

Since Python is strictly evaluated, we can do a little trick to turn things lazy:

Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))

d1 = lambda self: lambda: {
  'a': lambda: 3,
  'b': lambda: self()['a']()
}

# fix the d1, and evaluate it
d2 = Y(d1)()

# to get a
d2['a']() # 3

# to get b
d2['b']() # 3

Syntax wise this is not very nice. That's because of us needing to explicitly construct lazy expressions with lambda: ... and explicitly evaluate lazy expression with ...(). It's the opposite problem in lazy languages needing strictness annotations, here in Python we end up needing lazy annotations.

I think with some more meta-programmming and some more tricks, the above could be made more easy to use.

Note that this is basically how let-rec works in some functional languages.

The jsbueno answer in Python 3 :

class MyDict(dict):
    def __getitem__(self, item):
        return dict.__getitem__(self, item).format(self)

dictionary = MyDict({
    'user' : 'gnucom',
    'home' : '/home/{0[user]}',
    'bin' : '{0[home]}/bin' 
})

print(dictionary["home"])
print(dictionary["bin"])

Her ewe use the python 3 string formatting with curly braces {} and the .format() method.

Documentation : https://docs.python.org/3/library/string.html

Related