2

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

Dmitry
  • 93
  • 1
  • 7

4 Answers4

2

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.

1

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
etskinner
  • 160
  • 1
  • 7
  • Your solution is very interesting, I didn't know that and will keep it mind for the future, thank you.. The only one thing makes me some noise, in this particular case, is what if my service provider makes some changes in they API I'll have to modify all source code searching line by line for that json object. – Dmitry May 05 '19 at 21:39
  • Good point. You may want to consider using constants (see [PEP-8](https://www.python.org/dev/peps/pep-0008/#constants)), along with `getattr()`. Something like `VAR1_PATH = "ACRI.message.title"; var1 = reduce(getattr, name.split("."), x)` – etskinner May 07 '19 at 02:43
0

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"]
chepner
  • 497,756
  • 71
  • 530
  • 681
0

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
chanduthedev
  • 356
  • 2
  • 9