'How to fix "TypeError" - 'dict_keys' object is not subscriptable?

I am totally new to Python and I am just practicing and "copying" some github codes. While doing that I realized I am using the latest version of Python and the man from the video is using Python 2.7, so the codes he used are not compatible with mine. Please help me how to rewrite the code.

def add_friend(self,friend_id):
    check_req = requests.get('https://gift-f06e4.firebaseio.com/.json?orderBy="my_friend_id"&equalTo=' + friend_id)

    data = check_req.json()
    print(check_req.ok)
    print(check_req.json())
    if data == {}:
        self.root.ids['add_friend_screen'].ids['add_friend_label'].text = "Invalid friend ID"

    else:
        key = data.keys()[0]
        new_friend_id = data[key]['my_friend_id']
        print('New friend id is', new_friend_id)

The problem occurs on this line:

key = data.keys()[0]

I know this is not how I should write this part in Python 3, but I don't know how to make it work.



Solution 1:[1]

tl;dr: If you only have one element in the dictionary, then using list(data.keys())[0] is perfectly fine. If you have more than one element then the answer is: "It depends..." (because you will get one element at random).

edit: Additional detail for Python 3.7: Dictionary ordering has become deterministic and this fact has been made part of the language spec so you can rely on this in Python. Earlier versions of Python might already be deterministic, but that's implementation specific and not part of the spec so you shouldn't rely on this. Finally, while this is True for Python 3.7+, this is not necessarily the case for other programming languages so you need to be careful about making this a habit.


Your solution of converting it to a list seems fine to me for this particular use case.

I just would like to digress a bit into the reason why this is unsubscriptable, which may lead you to another solution without relying on [0].

nb: It would help to see an example of the JSON document returned from the HTTP GET call to make a more informed statement. I will continue with an informed guess:

From the code, I can safely say that the result from the GET call is a JSON document which contains an object (in other words: something surrounded in { and }). This translates to a Python dictionary when calling the .json() method.

Python dictionaries have no guaranteed ordering of the keys. So these two instances are considered identical (even though the ordering is different) which you can try in a Python console:

>>> a = {"a": 10, "b": 20}
>>> b = {"b": 20, "a": 10}
>>> a == b
True

In your code, you use data.keys()[0] which means: "Give me the first key of the dicitonary". But because the ordering is not guaranteed, asking for the "first" item does not really make sense. This is why in Python 3 it is no longer subscriptable. They prohibit it to prevent logical errors in the code.

As a side-note, I should say that since Python 3.7, the ordering in Python dictionaries is stable in certain conditions, but you should still not rely on this behaviour.

If you are 100% sure that the dictionary you are using has only one element, then converting to a list and taking the first element is completely safe. Alternatively using next() as mentioned by @rdas is also fine if you have only one element in the dictionary. If the dictionary has more than one element, this is not guaranteed to give you the same element on each call, you will just get "any one" element from it.

Additionally, if you are in control of the JSON structure, you can decide whether you want to return the items as dictionaries or lists. The latter would have a guaranteed ordering.

If you can edit your question with an example JSON document I can give more information.

Solution 2:[2]

Alright, I just made it work: instead of key = data.keys()[0], I tried key = list(data.keys())[0], and it seems to be working.

Still not sure if this is the proper way. I'll take any suggestions, advice, help. Thanks a lot!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 CristiFati