How to avoid hard-coding tons of dict key strings in Python

2.2k Views Asked by At

May be it is a common question, but I am new in python!

I am working on some libraries that handle json queries and asked myself if there is some best practice to avoid hard-coding tons of json keys like:


# just an example..
var1 = response.json()["ACRI"]["message"]["title"]
var2 = response.json()["ACRI"]["message"]["content"]
var3 = response.json()["ACRI"]["message"]["meta"]["timestamp"]

when I saw it, it didn't likes me and I created a class with constants like:


class _const:
    class headers:
        X_Auth_Token = "X-Auth-Token"
        X_Username = 'X-Username'
        X_Password = 'X-Password'

    class params:
        ID = "DocumentID"
        Symbols = "Symbols"
        ACRICode = "ACRICode"rketSegmentID"
        entries = "entries"
        depth = "depth"
        date = "date"
        meta = "meta"
        timestamp = "timestamp"
        message = "message"
        # ...

Is that a good practice? Is there something I don't understanding about python compiler?

Thanks

edit: I'm facing performance and/or memory consumption best practices especially

4

There are 4 best solutions below

0
chepner On

Each nested dict is a dict you can store a reference to.

resp = response.json()["ACRI"]["message"]
var1 = resp["title"]
var2 = resp["content"]
var3 = resp["meta"]["timestamp"]
0
Antti Haapala -- Слава Україні On

The performance should be least of your concerns, the maintainability is more important. A good reason to use a symbolic name for a string is that you cannot typo it that easily then, c.f.

KEY_SYMBOLS = "Symbols"

foo[KEY_SYMOBLS]

vs

foo["Symobls"]

An IDE or a linter can find the former typo and highlight it even before running the code, but not so likely the latter one.


When it comes to performance the most performant Python code is the one that does not store strings in variables. Upon compilation the distinct uses of the same string within the same module are merged to reference just one string constant:

>>> 'foo' is 'foo'
True

Still, the point raised in Chepner's answer remains: you need not resolve subdictionaries from the root unnecessarily.

2
etskinner On

Your initial strategy follows Zen of Python very well. It is very readable, and anyone who uses Python will understand it right away. Your second example, however, is overly complicated, and difficult to understand.

That said, it is possible to parse json so that it becomes an object with attributes instead of a dict with keys. Your code would then look something like this:

import json
from collections import namedtuple

x = json.loads(response, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))

var1 = x.ACRI.message.title
var2 = x.ACRI.message.content
var3 = x.ACRI.message.meta.timestamp

Alternatively, define a simple class that takes care of it:

import json

class x(object):
    def __init__(self, j):
        self.__dict__ = json.loads(j)

var1 = x.ACRI.message.title
var2 = x.ACRI.message.content
var3 = x.ACRI.message.meta.timestamp
0
chanduthedev On

If reading JSON data from a file, use the below code snippet.

with open("config.json") as json_data_file:
    json_text = json_data_file.read()
    json_data = json.loads(json_text, object_hook=lambda d: namedtuple('X', d.keys())(*d.values()))

# Access data using below format 
var1 = json_data.key1.subkey1
var2 = json_data.key2.subkey2