Home > Software engineering >  Why does a json file behave like this?
Why does a json file behave like this?

Time:10-07

There is a function (rui) that takes the number num and returns the tenth power of num. Task: to write a decorator that will cache the outputs from this function so that if a num that has already been submitted is fed to the input, the decorator will output the value from the hash table, so as not to perform the function itself. If the number is fed to the function (rui) for the first time, then the decorator will simply add num and rui(num) to the hash table. I store cache data in a json file.

import json

def decor(func):
    def wraps(num):

        with open('inf.json') as json_file:
            dictionary = json.load(json_file)

        if num not in dictionary:
            with open('inf.json', 'w') as json_file:
                dictionary.update({num : func(num)})
                json.dump(dictionary, json_file)
        return dictionary[num]
    return wraps




@decor
def rui(num):
    return num**10

Problem: The json file is now empty. Execute:

rui(10)
rui(11)
rui(10)

According to the idea, the json file should now be like this:

{"10" : 10000000000, "11" : 25937424601}

But the json file is like this:

{"10": 10000000000, "11": 25937424601, "10": 10000000000}

Execute again:

rui(11)
rui(10)
rui(10)
rui(15)
rui(15)

json file:

{"10": 10000000000, "11": 25937424601, "15": 576650390625, "15": 576650390625}

CodePudding user response:

OK, here's the problem: when you serialize to JSON, the keys must be strings. However, your inputs are int objects. I had never noticed this before, but something like {'10': 'x', 10:'y'} will get serialized to'{"10": "x", "10": "y"}'!

>>> import json
>>> json.dumps({'10': 'x', 10:'y'})
'{"10": "x", "10": "y"}'

To me, this just seems like aberrant behavior, but it seems to be a consequence of the fact that Python allows any hashable objects as a key, but JSON only accepts strings, and the JSON implementation just naively converts to string whatever key is there.

My suggestion is to simply use another serialization format, e.g. pickle, or alternatively, so some extra work around serialization:

import json

def decor(func):
    def wraps(num):

        with open('inf.json') as json_file:
            dictionary = dict(json.load(json_file))

        if num not in dictionary:
            with open('inf.json', 'w') as json_file:
                dictionary[num] = func(num)
                json.dump(list(dictionary.items()), json_file)
        return dictionary[num]
    return wraps

As an aside, you may want to reconsider whether the deserialization/serialization should happen on each decorated function call, or rather, only once.

CodePudding user response:

Simple, try as this, e.g. a list/dict comp to convert keys of the int to a str type:

>>> import json
>>> obj = {"10": 10000000000, "11": 25937424601, 10: 10000000000}
>>> json.dumps(obj)
'{"10": 10000000000, "11": 25937424601, "10": 10000000000}'
>>> # oh no, YIKES!
>>> obj = {str(k): v for k: v in obj.items()}
  File "<stdin>", line 1
    obj = {str(k): v for k: v in obj.items()} 
                          ^
SyntaxError: invalid syntax
>>> obj = {str(k): v for k, v in obj.items()}
>>> json.dumps(obj)
'{"10": 10000000000, "11": 25937424601}'
>>> # oh, SNAP!

For the data that is to be deeply nested, use a recursive function would be a good as the below shows it:

import json

def dump_it_plis(o):
    return json.dumps(inner_dump_it(o))
def inner_dump_it(o):
    return {str(k): inner_dump_it(v) if isinstance(v, dict)
    else [inner_dump_it(e) for e in v] if isinstance(v, list)
    else v
     for k, v in o.items()}

obj = {"lst": [{"10": 10000000000, "11": 25937424601, 10: 10000000000}]}
print(json.dumps(obj))
# this is print:
#  {"lst": [{"10": 10000000000, "11": 25937424601, "10": 10000000000}]}
print(dump_it_plis(obj))
# this is print:
#  {"lst": [{"10": 10000000000, "11": 25937424601}]}

CodePudding user response:

Solution

Try writing:

dictionary[num] = func(num)

Instead of:

dictionary.update({num : func(num)})

I don't know much about what you are trying to do but that is the only thing I can see is being weird.

Explaination

dictionary.update(), by assumption and inference of the issue, appends a new {key : value} to the dictionary, even if there is already a key of the same name present.

  • Related