What do I do when I need a self referential dictionary?
No fear of creating new classes -
You can take advantage of Python's string formating capabilities
and simply do:
class MyDict(dict):
def __getitem__(self, item):
return dict.__getitem__(self, item) % self
dictionary = MyDict({
'user' : 'gnucom',
'home' : '/home/%(user)s',
'bin' : '%(home)s/bin'
})
print dictionary["home"]
print dictionary["bin"]
Python self referential dictionary with class
self
doesn't just magically appear; Python functions are wrapped in methods when accessed via an instance (through invoking the function as a descriptor), and self
is then passed in to the function as a first argument.
For your lambdas, you'll need to pass self
in explicitly:
class ParamDict(dict):
def __getitem__(self, key):
val = dict.__getitem__(self, key)
return callable(val) and val(self) or val
params = ParamDict({
'a': 5,
'b': lambda self: 2**self['a']
})
Self-referential dictionary comprehension
In general you won't be able to reference the dictionary itself in the comprehension, because the name won't get assigned to the resulting dictionary until the comprehension is completed, so you'll have to settle for predefining the dictionary* and utilize mutating methods of the existing dictionary.
Since you're iterating over the input list, you'll need to update the existing dictionary with the new values whenever you come across the key. And as you can't use an assignment in the dictionary comprehension, you'll want to use the dict.update()
method (or __setitem__
or setdefault
). That method always returns None
, so you can utilize that to achieve the desired side effect in a number of different places inside the dictionary comprehension.
In particular, any filtering condition clause will be executed, so you can use that. Alternatively, expr or value
will evaluate the expression, which will always return None
, and since that's falsey the whole expression evaluates to value
, so you can place that expression in the key or value. That gives us the following possibilities:
With the side effect in the filter clause:
d = {}
d = {k: d[k] for k, *vals in x if d.update({k: d.get(k, []) + vals}) is None}
With the side effect in a expr or key
expression:
d = {}
d = {d.update({k: d.get(k, []) + vals}) or k: d[k] for k, *vals in x}
With the side effect in a expr or value
expression:
d = {}
d = {k: d.update({k: d.get(k, []) + vals}) or d[k] for k, *vals in x}
* Using assignment expressions (Python 3.8+), you can predefine the dictionary inside the comprehension itself with this abomination:
d = {k: d.update({k: d.get(k, []) + vals}) or d[k] for i, (k, *vals) in enumerate(x) if i or not (d := {})}
This uses enumerate()
to detect when you're on the first iteration, in which case an assignment expression can construct the dictionary that is used in the rest of the comprehension. After the first iteration, the assignment expression is not evaluated again so d
doesn't get reassigned in the course of the evaluation.
Note: Obviously, all of the methods shown in this answer are terrible. Side-effects inside comprehensions are unnecessary, unexpected, confusing, and in one word, silly. Do not use this code. But it is fun to see what's possible!
Dynamic Self Referencing Dictionary in Python
Something like this perhaps? Tested with Python 2.7.
from UserDict import UserDict
class Barn(UserDict):
'''Barn automatically sets correct values for chickens.
TBD: What to do if chickens get changed?'''
def __init__(self, *args, **kw):
UserDict.__init__(self, *args, **kw)
pigs = self.data.get('pigs', None)
if pigs is not None:
self.data['chicken'] = pigs * 2
def __setitem__(self, name, value):
if name == 'pigs':
self.data['chicken'] = value * 2
self.data[name] = value
if __name__ == '__main__':
b = Barn(cows=1, pigs=2)
print b
b['pigs'] = 3
print b
Running that should produce something like this:
{'cows': 1, 'chicken': 4, 'pigs': 2}
{'cows': 1, 'chicken': 6, 'pigs': 3}
Is there any usage of self-referential lists or circular reference in list, eg. appending a list to itself
The use case is that Python is a dynamically typed language, where anything can reference anything, including itself.
List elements are references to other objects, just like variable names and attributes and the keys and values in dictionaries. The references are not typed, variables or lists are not restricted to only referencing, say, integers or floating point values. Every reference can reference any valid Python object. (Python is also strongly typed, in that the objects have a specific type that won't just change; strings remain strings, lists stay lists).
So, because Python is dynamically typed, the following:
foo = []
# ...
foo = False
is valid, because foo
isn't restricted to a specific type of object, and the same goes for Python list objects.
The moment your language allows this, you have to account for recursive structures, because containers are allowed to reference themselves, directly or indirectly. The list
representation takes this into account by not blowing up when you do this and ask for a string representation. It is instead showing you a [...]
entry when there is a circular reference. This happens not just for direct references either, you can create an indirect reference too:
>>> foo = []
>>> bar = []
>>> foo.append(bar)
>>> bar.append(foo)
>>> foo
[[[...]]]
foo
is the outermost [
/]
] pair and the [...]
entry. bar
is the [
/]
pair in the middle.
There are plenty of practical situations where you'd want a self-referencing (circular) structure. The built-in OrderedDict
object uses a circular linked list to track item order, for example. This is not normally easily visible as there is a C-optimised version of the type, but we can force the Python interpreter to use the pure-Python version (you want to use a fresh interpreter, this is kind-of hackish):
>>> import sys
>>> class ImportFailedModule:
... def __getattr__(self, name):
... raise ImportError
...
>>> sys.modules["_collections"] = ImportFailedModule() # block the extension module from being loaded
>>> del sys.modules["collections"] # force a re-import
>>> from collections import OrderedDict
now we have a pure-python version we can introspect:
>>> od = OrderedDict()
>>> vars(od)
{'_OrderedDict__hardroot': <collections._Link object at 0x10a854e00>, '_OrderedDict__root': <weakproxy at 0x10a861130 to _Link at 0x10a854e00>, '_OrderedDict__map': {}}
Because this ordered dict is empty, the root references itself:
>>> od._OrderedDict__root.next is od._OrderedDict__root
True
just like a list can reference itself. Add a key or two and the linked list grows, but remains linked to itself, eventually:
>>> od["foo"] = "bar"
>>> od._OrderedDict__root.next is od._OrderedDict__root
False
>>> od._OrderedDict__root.next.next is od._OrderedDict__root
True
>>> od["spam"] = 42
>>> od._OrderedDict__root.next.next is od._OrderedDict__root
False
>>> od._OrderedDict__root.next.next.next is od._OrderedDict__root
True
The circular linked list makes it easy to alter the key ordering without having to rebuild the whole underlying hash table.
Reference a dictionary within itself
Why not update the dictionary:
my_dict = {'root': '/var/tmp'}
my_dict.update({'file': os.path.join(my_dict.get('root'), 'file')})
Don't use dict
as a name. You may need the real dict
builtin later on.
Is there a pythonic way of referring to the current object (self-reference) we are declaring with (some) Pythons built-in types?
Python provides no way to refer to an object under construction by a literal or a display*. You can (ab)use the assignment expression in Python 3.8 or later to simulate this:
a_dictionary = {
"key_1": (x := "value_1"),
"key_2": (y := "value_2"),
"key_3": x + "_" + y
}
It requires some planning ahead, as you are not referring to a key value directly, rather a pre-defined variable. Notice that x
and y
remain in scope after the assignment to a_dictionary
, so this is just a questionable equivalent of
x = "value_1"
y = "value_2"
a_dictionary = {
"key_1": x,
"key_2": y,
"key_3": x + "_" + y
}
A custom class would really be more appropriate:
class Thing:
def __init__(self, v1, v2):
self.key_1 = v1
self.key_2 = v2
self.key_3 = v1 + "_" + v2
a_thing = Thing("value_1", "value_2")
- A display is a construct like a literal, but could contain non-literal references. For example, list displays include
[1, x, y]
and[int(x) for x in foo]
.
Pythonic way to walk a self-referential dictionary
To "walk" the dictionary, just do the lookups in a loop until there are no more:
>>> def walk(d, val):
while val in d:
val = d[val]
return None if val == '-' else val
>>> d = {'1': '-', '0': '6', '3': '1', '2': '3', '4': '5', '6': '9'}
>>> print {k: walk(d, k) for k in d}
{'1': None, '0': '9', '3': None, '2': None, '4': '5', '6': '9'}
What do I do when I need a self referential dictionary?
No fear of creating new classes -
You can take advantage of Python's string formating capabilities
and simply do:
class MyDict(dict):
def __getitem__(self, item):
return dict.__getitem__(self, item) % self
dictionary = MyDict({
'user' : 'gnucom',
'home' : '/home/%(user)s',
'bin' : '%(home)s/bin'
})
print dictionary["home"]
print dictionary["bin"]
Related Topics
Typeerror: Str Does Not Support Buffer Interface
Is There a Description of How _Cmp_ Works for Dict Objects in Python 2
Executing Command Using Paramiko Exec_Command on Device Is Not Working
Testing Floating Point Equality
Python Dictionary:Typeerror: Unhashable Type: 'List'
Importing Pyspark in Python Shell
Python How to Write to a Binary File
Executing Multiple Statements with Postgresql via SQLalchemy Does Not Persist Changes
How to Get Around Declaring an Unused Variable in a for Loop
Deleting List Elements Based on Condition
Convert Floating Point Number to a Certain Precision, and Then Copy to String
File Not Found Error When Launching a Subprocess Containing Piped Commands
What Is the Advantage of a List Comprehension Over a for Loop
How to Get a Raw, Compiled SQL Query from a SQLalchemy Expression
Type Hint for a Function That Returns Only a Specific Set of Values