Python Reference Dictionary Key in same Dictionary - python

I'm trying to access a key in a dictionary before "declaring" it.
Similar to this:
test_dict = {'path': '/root/secret/', 'path2': test_dict['path']+'meow/'}
I am aware that I could accomplish this by doing in the next line, like:
test_dict['path2'] = test_dict['path']+'meow'
however for readability i would prefer writing all the keys in the dict for a config file.
Is this possible in Python?

Convince yourself that this is not possible. You cannot refer to an object that hasn't even been created. What you can, however, do, is use a string variable. This should do what you want relatively easily.
p = '/root/secret/'
test_dict = {'path' : p, 'path2' : os.path.join(p, 'meow')}
Also, it's good practice to use os.path.join when concatenating sub-paths together.

#cᴏʟᴅsᴘᴇᴇᴅ, I think this is more readable, imagine if OP were to add 15 paths.
p = '/root/secret/'
# initiate dict
test_dict = {}
# assign values
test_dict['path'] = p
test_dict['path2'] = os.path.join(p, 'meow')

Related

How to reset value of multiple dictionaries elegantly in python

I am working on a code which pulls data from database and based on the different type of tables , store the data in dictionary for further usage.
This code handles around 20-30 different table so there are 20-30 dictionaries and few lists which I have defined as class variables for further usage in code.
for example.
class ImplVars(object):
#dictionary capturing data from Asset-Feed table
general_feed_dict = {}
ports_feed_dict = {}
vulns_feed_dict = {}
app_list = []
...
I want to clear these dictionaries before I add data in it.
Easiest or common way is to use clear() function but this code is repeatable as I will have to write for each dict.
Another option I am exploring is with using dir() function but its returning variable names as string.
Is there any elegant method which will allow me to fetch all these class variables and clear them ?
You can use introspection as you suggest:
for d in filter(dict.__instancecheck__, ImplVars.__dict__.values()):
d.clear()
Or less cryptic, covering lists and dicts:
for obj in ImplVars.__dict__.values():
if isinstance(obj, (list, dict)):
obj.clear()
But I would recommend you choose a bit of a different data structure so you can be more explicit:
class ImplVars(object):
data_dicts = {
"general_feed_dict": {},
"ports_feed_dict": {},
"vulns_feed_dict": {},
}
Now you can explicitly loop over ImplVars.data_dicts.values and still have other class variables that you may not want to clear.
code:
a_dict = {1:2}
b_dict = {2:4}
c_list = [3,6]
vars_copy = vars().copy()
for variable, value in vars_copy.items():
if variable.endswith("_dict"):
vars()[variable] = {}
elif variable.endswith("_list"):
vars()[variable] = []
print(a_dict)
print(b_dict)
print(c_list)
result:
{}
{}
[]
Maybe one of the easier kinds of implementation would be to create a list of dictionaries and lists you want to clear and later make the loop clear them all.
d = [general_feed_dict, ports_feed_dict, vulns_feed_dict, app_list]
for element in d:
element.clear()
You could also use list comprehension for that.

Python set dictionary nested key with dot delineated string

If I have a dictionary that is nested, and I pass in a string like "key1.key2.key3" which would translate to:
myDict["key1"]["key2"]["key3"]
What would be an elegant way to be able to have a method where I could pass on that string and it would translate to that key assignment? Something like
myDict.set_nested('key1.key2.key3', someValue)
Using only builtin stuff:
def set(my_dict, key_string, value):
"""Given `foo`, 'key1.key2.key3', 'something', set foo['key1']['key2']['key3'] = 'something'"""
# Start off pointing at the original dictionary that was passed in.
here = my_dict
# Turn the string of key names into a list of strings.
keys = key_string.split(".")
# For every key *before* the last one, we concentrate on navigating through the dictionary.
for key in keys[:-1]:
# Try to find here[key]. If it doesn't exist, create it with an empty dictionary. Then,
# update our `here` pointer to refer to the thing we just found (or created).
here = here.setdefault(key, {})
# Finally, set the final key to the given value
here[keys[-1]] = value
myDict = {}
set(myDict, "key1.key2.key3", "some_value")
assert myDict == {"key1": {"key2": {"key3": "some_value"}}}
This traverses myDict one key at a time, ensuring that each sub-key refers to a nested dictionary.
You could also solve this recursively, but then you risk RecursionError exceptions without any real benefit.
There are a number of existing modules that will already do this, or something very much like it. For example, the jmespath module will resolve jmespath expressions, so given:
>>> mydict={'key1': {'key2': {'key3': 'value'}}}
You can run:
>>> import jmespath
>>> jmespath.search('key1.key2.key3', mydict)
'value'
The jsonpointer module does something similar, although it likes / for a separator instead of ..
Given the number of pre-existing modules I would avoid trying to write your own code to do this.
EDIT: OP's clarification makes it clear that this answer isn't what he's looking for. I'm leaving it up here for people who find it by title.
I implemented a class that did this a while back... it should serve your purposes.
I achieved this by overriding the default getattr/setattr functions for an object.
Check it out! AndroxxTraxxon/cfgutils
This lets you do some code like the following...
from cfgutils import obj
a = obj({
"b": 123,
"c": "apple",
"d": {
"e": "nested dictionary value"
}
})
print(a.d.e)
>>> nested dictionary value

Create many empty dictionary in Python

I'm trying to create many dictionaries in a for loop in Python 2.7. I have a list as follows:
sections = ['main', 'errdict', 'excdict']
I want to access these variables, and create new dictionaries with the variable names. I could only access the list sections and store an empty dictionary in the list but not in the respective variables.
for i in enumerate(sections):
sections[i] = dict()
The point of this question is. I'm going to obtain the list sections from a .ini file, and that variable will vary. And I can create an array of dictionaries, but that doesn't work well will the further function requirements. Hence, my doubt.
Robin Spiess answered your question beautifully.
I just want to add the one-liner way:
section_dict = {sec : {} for sec in sections}
For maintaining the order of insertion, you'll need an OrderedDict:
from collections import OrderedDict
section_dict = OrderedDict((sec, {}) for sec in sections)
To clear dictionaries
If the variables in your list are already dictionaries use:
for var in sections:
var.clear()
Note that here var = {} does not work, see Difference between dict.clear() and assigning {} in Python.
To create new dictionaries
As long as you only have a handful of dicts, the best way is probably the easiest one:
main = {} #same meaning as main = dict() but slightly faster
errdict = {}
excdict = {}
sections = [main,errdict,excdict]
The variables need to be declared first before you can put them in a list.
For more dicts I support #dslack's answer in the comments (all credit to him):
sections = [dict() for _ in range(numberOfDictsYouWant)]
If you want to be able to access the dictionaries by name, the easiest way is to make a dictionary of dictionaries:
sectionsdict = {}
for var in sections:
sectionsdict[var] = {}
You might also be interested in: Using a string variable as a variable name

Pythonic way to get the index of element from a list of dicts depending on multiple keys

I am very new to python, and I have the following problem. I came up with the following solution. I am wondering whether it is "pythonic" or not. If not, what would be the best solution ?
The problem is :
I have a list of dict
each dict has at least three items
I want to find the position in the list of the dict with specific three values
This is my python example
import collections
import random
# lets build the list, for the example
dicts = []
dicts.append({'idName':'NA','idGroup':'GA','idFamily':'FA'})
dicts.append({'idName':'NA','idGroup':'GA','idFamily':'FB'})
dicts.append({'idName':'NA','idGroup':'GB','idFamily':'FA'})
dicts.append({'idName':'NA','idGroup':'GB','idFamily':'FB'})
dicts.append({'idName':'NB','idGroup':'GA','idFamily':'FA'})
dicts.append({'idName':'NB','idGroup':'GA','idFamily':'FB'})
dicts.append({'idName':'NB','idGroup':'GB','idFamily':'FA'})
dicts.append({'idName':'NB','idGroup':'GB','idFamily':'FB'})
# let's shuffle it, again for example
random.shuffle(dicts)
# now I want to have for each combination the index
# I use a recursive defaultdict definition
# because it permits creating a dict of dict
# even if it is not initialized
def tree(): return collections.defaultdict(tree)
# initiate mapping
mapping = tree()
# fill the mapping
for i,d in enumerate(dicts):
idFamily = d['idFamily']
idGroup = d['idGroup']
idName = d['idName']
mapping[idName][idGroup][idFamily] = i
# I end up with the mapping providing me with the index within
# list of dicts
Looks reasonable to me, but perhaps a little too much. You could instead do:
mapping = {
(d['idName'], d['idGroup'], d['idFamily']) : i
for i, d in enumerate(dicts)
}
Then access it with mapping['NA', 'GA', 'FA'] instead of mapping['NA']['GA']['FA']. But it really depends how you're planning to use the mapping. If you need to be able to take mapping['NA'] and use it as a dictionary then what you have is fine.

List of dictionaries, in a dictionary - in Python

I have a case where I need to construct following structure programmatically (yes I am aware of .setdefault and defaultdict but I can not get what I want)
I basically need a dictionary, with a dictionary of dictionaries created within the loop.
At the beginning the structure is completely blank.
structure sample (please note, I want to create an array that has this structure in the code!)
RULE = {
'hard_failure': {
4514 : {
'f_expr' = 'ABC',
'c_expr' = 'XF0',
}
}
}
pseudo code that needs to create this:
...
self.rules = {}
for row in rows:
a = 'hard_failure'
b = row[0] # 4514
c = row[1] # ABC
d = row[2] # XF0
# Universe collapse right after
self.rules = ????
...
The code above is obviously not working since I dont know how to do it!
Example, that you've posted is not a valid python code, I could only imagine that you're trying to do something like this:
self.rules[a] = [{b:{'f_expr': c, 'c_expr': d}}]
this way self.rules is a dictionary of a list of a dictionary of a dictionary. I bet there is more sane way to do this.
rules = {}
failure = 'hard_failure'
rules[failure] = []
for row in rows:
#this is what people are referring to below. You left out the addition of the dictionary structure to the list.
rules[failure][row[0]] = {}
rules[failure][row[0]]['type 1'] = row[1]
rules[failure][row[0]]['type 2'] = row[2]
This is what I created based on how I understood the questions. I wasn't sure what to call the 'f_expr' and 'c_expr' since you never mention where you get those but I assume they are already know column names in a resultset or structure of some sort.
Just keep adding to the structure as you go.
Your example code doesn't seem to be valid Python. It's not clear if the second level element is supposed to be a list or a dictionary.
However, if you're doing what I think you're doing, and it's a dictionary, you could use a tuple as a key in the top-level dictionary instead of nesting dictionaries:
>>> a = 'hard_failure'
>>> b = 4514
>>> c = "ABC"
>>> d = "XF0"
>>> rules = {}
>>> rules[(a,b)] = {'f_expr' : a,'c_expr' : d}
>>> rules
{('hard_failure', 4514): {'c_expr': 'XF0', 'f_expr': 'hard_failure'}}
My favorite way to deal with nested dictionaries & lists of dictionaries is to use PyYAML. See this response for details.
Well, I apologize for the confusion, I never claimed that code actually compiled, hence (pseudo). Arthur Thomas put me on the right track, here is slightly modified version. (Yes, now its a simply nested dictionary, 3 levels down)
RULE_k = 'hard_failure'
self.rules = {}
for row in rows:
self.rules_compiled.setdefault(RULE_k, {})
self.rules_compiled[RULE_k][row[1]] = {}
self.rules_compiled[RULE_k][row[1]]['f_expr'] = row[0]
self.rules_compiled[RULE_k][row[1]]['c_expr'] = row[1]

Categories