How can I link a variable to a dictionary value in Python?
Consider the following code:
a_var = 10
a_dict = {'varfield':a_var, 'first':25, 'second':57}
# a_dict['varfield'] == 10 now
a_var = 700
# a_dict['varfield'] == 10 anyway
So is there a way to link the value of a variable to a field in a dictictionary without looking up for that field an updating it's value manually?
You would need to set the value of the dictionary key, to an object that you can change the value of.
For example like this:
class valueContainer(object):
def __init__(self, value):
self.value = value
def __repr__(self):
return self.value.__repr__()
v1 = valueContainer(1)
myDict = {'myvar': v1}
print myDict
#{'myvar': 1}
v1.value = 2
print myDict
#{'myvar': 2}
w = [410]
myDict = {'myvar': w}
print myDict
#{'myvar': [410]}
w[0] = 520
print myDict
#{'myvar': [520]}
That's the version of the code of M4rtini with a list instead of an instance of a class.
He is obliged to modify v1 (in fact its attribute value) with the instruction v1.value = ...,
I am obliged to modify the value in the list with w[0] = ...
The reason to act like this is that what you erroneously called a variable, and that is in fact an identifier, doesn't designates a variable in the sense of a "chunk of memory whose content can change" but references an object to which the identifier is binded, object whose value cannot change because it is an immutable object.
Please read the explanations of the documentation on the data model and the execution model of Python which is quite different from the ones of languages such as Java, PHP, etc.
Related
I know there's a similar question:
How to append to a dictionary of dictionaries
but the answers aren't working for me. My problem is as follows. If I need to add a new key: value pair to a Python dictionary, then
my_dict[key] = value
is always safe (as long as key is not a mutable type), whether my_dict had been already initialized or not.
However, if I want my_dict to be a dictionary of dictionaries, then
my_dict[keyA][keyB] = value
doesn't work, unless I already initialized my_dict[keyA] as an empty dictionary. So what I'm doing right now is:
class dict_of_dict():
def __init__(self):
self.ddict = {}
def update(self, keyA, keyB, value):
if not(keyA in self.ddict.keys()):
self.ddict[keyA] = {}
self.ddict[keyA][keyB] = value
a = dict_of_dict()
a.update(0, 3, "foobar")
a.ddict
This works, but I feel like it's overkill. Is there a more compact/Pythonic but still readable solution?
Is it possible to create a dynamic linked variable? So that changes in original_VAR will automatically take effect in copied_VAR ? Like so:
original_VAR = 'original_VAL'
copied_VAR = original_VAR
original_VAR = 'modified_VAL'
print(copied_VAR)
#desired output:
>>>> 'modified_VAL'
A similar behavior can be created for lists under few conditions:
original_DICT_ARR = [{'key': 'original_VAL'}]
# 1 - does not create a dynamic link
copied_DICT_ARR = [value for value in original_DICT_ARR]
# 2 - does create a dynamic link
copied_DICT_ARR = original_DICT_ARR
# 3 - does create a dynamic link, if the copied element is a list or dict, but not if string, boolean, int, float
copied_DICT_ARR = []
copied_DICT_ARR.append(original_DICT_ARR[0])
# MODIFICATION:
original_DICT_ARR[0]['key'] = 'modified_VAL'
# RESULT for 2,3
print(copied_DICT_ARR[0])
>>>> {'key': 'modified_VAL'}
Why would I want to do this?
I am building a list, the list is full of dict objects. I need to assign a value to a certain dict key.
Later, that value might change - I don't want to loop through all dictionaries in the list again. I want to change the original variable, and have the effect taken place in all dictionaries automatically.
You can keep a reference the specific dict you want to modify later. Since it refers to the same underlying dict - changes will be reflected in your reference. Like so:
original_DICT_ARR = [{'key': 'original_VAL'}, {'key': 'another_val'}]
target_dict = original_DICT_ARR[0]
# MODIFICATION:
original_DICT_ARR[0]['key'] = 'modified_VAL'
# RESULT for 2,3
print(target_dict)
Gives:
{'key': 'modified_VAL'}
You could achieve this and keep things abstract by using a mutable object and subclassing UserDict and overriding __getitem__:
class ChangingVal:
def __init__(self, val):
self.val = val
class ChangingValsDict(UserDict):
def __getitem__(self, key):
ret = self.data[key]
if isinstance(ret, ChangingVal):
ret = ret.val
return ret
my_dict = ChangingValsDict()
changing_val = ChangingVal(3)
my_dict["changing_value"] = changing_val
print(my_dict["changing_value"]) # -- outputs "3"
# change your value
changing_val.val = 6
print(my_dict["changing_value"]) # -- outputs "6"
As a total beginner I'm quite proud of this function. Although I believe there's probably an easier, more pythonic way of doing the exact same thing:
Genes = ['Gen1', 'Gen2', 'Gen3']
Mutations = ['Gen1.A', 'Gen1.B', 'Gen2.A', 'Gen3.A', 'Gen3.B', 'Gen3.C']
def RawDict(keys, values):
dictKeys = []
dictValues = []
for key in keys:
keyVal = []
for value in values:
if value.find(key) == -1:
pass
else:
keyVal.append(value)
dictKeys.append(key)
dictValues.append(keyVal)
return zip(dictKeys, dictValues)
GenDict = dict(RawDict(Genes, Mutations))
print(GenDict)
The function above is a rather overcomplicated (I think) way of putting several values (mutations) within keys (genes). However I was wondering if I could tweak this so I could get a dictionary by just doing this:
dict(GenDict, Genes, Mutations)
print(GenDict)
My struggle involves that when I use dict within the function, this won't work:
Genes = ['Gen1', 'Gen2', 'Gen3']
Mutations = ['Gen1.A', 'Gen1.B', 'Gen2.A', 'Gen3.A', 'Gen3.B', 'Gen3.C']
def fullDict(dictName, keys, values):
dictKeys = []
dictValues = []
for key in keys:
keyVal = []
for value in values:
if value.find(key) == -1:
pass
else:
keyVal.append(value)
dictKeys.append(key)
dictValues.append(keyVal)
dictName = dict(RawDict(Genes, Mutations))
fullDict(GenDict, Genes, Mutations)
print(GenDict)
The above just won't work as GenDict is not defined.
From what I understand, you want to move from this:
gen_dict = make_dictionary(genes, mutations)
to this:
make_dictionary(gen_dict, genes, mutations)
where the make_dictionary function "creates" the variable gen_dict.
Unfortunately, this isn't really how variables work. If you want to define a variable called GenDict, the way to do this is to use GenDict = .... You could do something like this:
gen_dict = {}
fill_dictionary(gen_dict, genes, mutations)
This creates a variable called gen_dict and assigns it to a new, empty dictionary. Your function would then go through and add things to that dictionary:
def fill_dictionary(d, genes, mutations):
for g in genes:
d[g] = [m for m in mutations if m.startswith(g)]
But calling a function cannot cause a new variable to appear in the caller's scope. (This is not completely true, because of globals(), but for most intents and purposes, it is.)
(By the way, there is a one-liner that will create the dictionary: dictionary = { g : [m for m in mutations if m.startswith(g+".")] for g in genes }. Search for list comprehensions and dictionary comprehensions on Google or StackOverflow -- they are amazing!)
I am assuming that you want the "Gen"s to be stored by the numerical value that it contains.
Genes = ['Gen1', 'Gen2', 'Gen3']
Mutations = ['Gen1.A', 'Gen1.B', 'Gen2.A', 'Gen3.A', 'Gen3.B', 'Gen3.C']
the_dict = {i:[] for i in Genes}
for i in Mutations:
new_val = i.split(".")
the_dict[new_val[0]].append(i)
print(the_dict)
Output:
{'Gen2': ['Gen2.A'], 'Gen3': ['Gen3.A', 'Gen3.B', 'Gen3.C'], 'Gen1': ['Gen1.A', 'Gen1.B']}
I assume you have a background in programming in some other language than Python; a language that lets you change function parameters. Well, Python does not. The problem is not with the use of dict, but rather with the fact that you're assigning to a function parameter. This will not have an effect outside the function. What you want to do is probably this:
def fullDict(keys, values):
return { key: [ value for value in values if key in value] for key in keys }
print(fullDict(Genes, Mutations))
Requirements:
There is a variable, for example, related_to_dict = 10
Construct a key value pair data, for example, special_dict = {0 :
ref_related_to_dict}
When the variable of related_to_dict changed, the value of
special_dict[0] also changed to the value of related_to_dict
accordingly.
When the value_of special_dict[0], e.g. ref_related_to_dict changed, the
value of related_to_dict also changed to the value of
special_dict[0] accordingly.
Is there a way to achieve this task?
You need to wrap the value in some sort of container.
class Ref:
def __init__(self, v):
self.val = v
And then:
related_to_dict = Ref(10)
special_dict = {0: related_to_dict}
Then it works as desired:
related_to_dict.val = 40
print(special_dict[0].val) # 40
[Python 3.4.2]
I know this question sounds ridiculous, but I can't figure out where I'm messing up. I'm trying to add keys and values to a dictionary by using strings instead of quoted text. So instead of this,
dict['key'] = value
this:
dict[key] = value
When I run the command above, I get this error:
TypeError: 'str' object does not support item assignment
I think Python is thinking that I'm trying to create a string, not add to a dictionary. I'm guessing I'm using the wrong syntax. This is what I'm trying to do:
dict[string_for_key][string_for_value] = string_for_deeper_value
I want this^ command to do this:
dict = {string_for_key: string_for_value: string_for_deeper_value}
I'm getting this error:
TypeError: 'str' object does not support item assignment
I should probably give some more context. I'm:
creating one dictionary
creating a copy of it (because I need to edit the dictionary while iterating through it)
iterating through the first dictionary while running some queries
trying to assign a query's result as a value for each "key: value" in the dictionary.
Here's a picture to show what I mean:
key: value: query_as_new_value
-----EDIT-----
Sorry, I should have clarified: the dictionary's name is not actually 'dict'; I called it 'dict' in my question to show that it was a dictionary.
-----EDIT-----
I'll just post the whole process I'm writing in my script. The error occurs during the last command of the function. Commented out at the very bottom are some other things I've tried.
from collections import defaultdict
global query_line, pericope_p, pericope_f, pericope_e, pericope_g
def _pre_query(self, typ):
with open(self) as f:
i = 1
for line in f:
if i == query_line:
break
i += 1
target = repr(line.strip())
###skipping some code
pericope_dict_post[self][typ] = line.strip()
#^Outputs error TypeError: 'str' object does not support item assignment
return
pericope_dict_pre = {'pericope-p.txt': 'pericope_p',
'pericope-f.txt': 'pericope_f',
'pericope-e.txt': 'pericope_e',
'pericope-g.txt': 'pericope_g'}
pericope_dict_post = defaultdict(dict)
#pericope_dict_post = defaultdict(list)
#pericope_dict_post = {}
for key, value in pericope_dict_pre.items():
pericope_dict_post[key] = value
#^Works
#pericope_dict_post.update({key: value})
#^Also works
#pericope_dict_post.append(key)
#^AttributeError: 'dict' object has no attribute 'append'
#pericope_dict_post[key].append(value)
#^AttributeError: 'dict' object has no attribute 'append'
_pre_query(key, value)
-----FINAL EDIT-----
Matthias helped me figure it out, although acushner had the solution too. I was trying to make the dictionary three "levels" deep, but Python dictionaries cannot work this way. Instead, I needed to create a nested dictionary. To use an illustration, I was trying to do {key: value: value} when I needed to do {key: {key: value}}.
To apply this to my code, I need to create the [second] dictionary with all three strings at once. So instead of this:
my_dict[key] = value1
my_dict[key][value1] = value2
I need to do this:
my_dict[key][value1] = value2
Thanks a ton for all your help guys!
You could create a dictionary that expands by itself (Python 3 required).
class AutoTree(dict):
"""Dictionary with unlimited levels"""
def __missing__(self, key):
value = self[key] = type(self)()
return value
Use it like this.
data = AutoTree()
data['a']['b'] = 'foo'
print(data)
Result
{'a': {'b': 'foo'}}
Now I'm going to explain your problem with the message TypeError: 'str' object does not support item assignment.
This code will work
from collections import defaultdict
data = defaultdict(dict)
data['a']['b'] = 'c'
data['a'] doesn't exist, so the default value dict is used. Now data['a'] is a dict and this dictionary gets a new value with the key 'b' and the value 'c'.
This code won't work
from collections import defaultdict
data = defaultdict(dict)
data['a'] = 'c'
data['a']['b'] = 'c'
The value of data['a'] is defined as the string 'c'. Now you can only perform string operations with data['a']. You can't use it as a dictionary now and that's why data['a']['b'] = 'c' fails.
first, do not use dict as your variable name as it shadows the built-in of the same name.
second, all you want is a nested dictionary, no?
from collections import defaultdict
d = defaultdict(dict)
d[string_for_key][string_for_value] = 'snth'
another way, as #Matthias suggested, is to create a bottomless dictionary:
dd = lambda: defaultdict(dd)
d = dd()
d[string_for_key][string_for_value] = 'snth'
you can do something like this:
>>> my_dict = {}
>>> key = 'a' # if key is not defined before it will raise NameError
>>> my_dict[key] = [1]
>>> my_dict[key].append(2)
>>> my_dict
{'a': [1, 2]}
Note: dict is inbuilt don't use it as variable name