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.