8

[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:

  1. creating one dictionary
  2. creating a copy of it (because I need to edit the dictionary while iterating through it)
  3. iterating through the first dictionary while running some queries
  4. 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!

2
  • if key is not defined before you will get error Commented Dec 3, 2014 at 19:31
  • Yeah, it's already defined. All three strings (string_for_key, string_for_value, and string_for_deeper_value) are already defined. The dictionary already contains the key pointed to by string_for_key and the value pointed to by string_for_key. I'm trying to insert string_for_deeper_value into the dictionary as well. Commented Dec 3, 2014 at 19:37

3 Answers 3

6

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.

Sign up to request clarification or add additional context in comments.

12 Comments

you don't even need a class for that. dd = lambda: defaultdict(dd)
@acushner: I upvoted your answer because that ist what I would do. My code is just a part from a more complex class and might come handy sometimes.
Each of the AutoTree and dd = lambda: defaultdict(dd) options still give me TypeError: list indices must be integers, not str. Thanks for all the help though guys. I think the problem is that I'm using a string instead of text on the right hand side of the equation. So my_dict[key][value] = string instead of my_dict[key][value] = 'text'
that is not it. at all. either my_dict is a list or my_dict[key] is a list. that is your problem.
@GreenRaccoon23: The error TypeError: list indices must be integers, not str has nothing to with the dictionary code. There is no list involved.
|
1

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'

3 Comments

Thanks, I've tried this, but instead of d[string_for_key][string_for_value] = 'snth' I'm using d[string_for_key][string_for_value] = snth. It gives me the error TypeError: list indices must be integers, not str
you are creating a list somewhere. check the type of your objects
Yeah, sorry, I meant TypeError: 'str' object does not support item assignment. I did this in another comment too. :S
0

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

2 Comments

Thanks, I've tried this, but I get the error AttributeError: 'dict' object has no attribute 'append'
Sure, I added some of my script into my original question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.