我一直在尝试将大量(> 800mb)数据写入JSON文件;我做了一些相当多的试验和错误来获得此代码:
def write_to_cube(data):
with open('test.json') as file1:
temp_data = json.load(file1)
temp_data.update(data)
file1.close()
with open('test.json', 'w') as f:
json.dump(temp_data, f)
f.close()
Run Code Online (Sandbox Code Playgroud)
运行它只需调用该函数write_to_cube({"some_data" = data})
现在这段代码的问题是,对于少量数据来说速度很快,但是当test.json文件超过 800mb 时就会出现问题。当我尝试更新或添加数据时,需要很长时间。
我知道有外部库,例如simplejson或jsonpickle,我不太确定如何使用它们。
还有其他办法解决这个问题吗?
更新:
我不确定这怎么可能是重复的,其他文章没有提到编写或更新大型 JSON 文件,而是只提到了解析。
有没有一种内存高效且快速的方法来在 python 中加载大 json 文件?
上述任何一个都不能重复解决这个问题。他们没有谈论任何有关写作或更新的事情。
我发现json-stream包可能会有所帮助。虽然它确实提供了跳过输入 JSON 并将 Python 数据结构流式传输到输出 JSON 文件的机制,但如果没有 OP 的具体细节,很难说这是否能满足他们的需求。
为了看看它在处理大文件时是否确实具有内存优势,我模拟了这个基本的 JSON:
{
"0": {"foo": "bar"},
"1": {"foo": "bar"},
"2": {"foo": "bar"},
"3": {"foo": "bar"},
...
Run Code Online (Sandbox Code Playgroud)
最多 10M 个对象:
...
"9999997": {"foo": "bar"},
"9999998": {"foo": "bar"},
"9999999": {"foo": "bar"},
}
Run Code Online (Sandbox Code Playgroud)
我已经提出将每个奇怪的对象更改为的要求{"foo": "BAR"}:
{
"0": {"foo": "bar"},
"1": {"foo": "BAR"},
"2": {"foo": "bar"},
"3": {"foo": "BAR"},
...
"9999997": {"foo": "BAR"},
"9999998": {"foo": "bar"},
"9999999": {"foo": "BAR"},
}
Run Code Online (Sandbox Code Playgroud)
我确信这比 OP 需要通过传递更新字典(我想象它有一个适度的“深度”结构)来完成的事情更琐碎。
我编写了脚本来处理一些测试文章的生成、读取和转换:
产生:
@streamable_dict
def yield_obj(n: int):
for x in range(n):
yield str(x), {"foo": "bar"}
def gen_standard(n: int):
with open(f"gen/{n}.json", "w") as f:
obj = dict(list(yield_obj(n)))
json.dump(obj, f, indent=1)
def gen_stream(n: int):
with open(f"gen/{n}.json", "w") as f:
json.dump(yield_obj(n), f, indent=1)
Run Code Online (Sandbox Code Playgroud)
yield_obj()是一个迭代器,可以使用 实现,并在包装器的帮助下流dict(list(...))式传输到标准方法。json.dump()@streamable_dict
制作三个测试文件:
-rw-r--r-- 1 zyoung staff 2.9M Feb 23 17:24 100000.json
-rw-r--r-- 1 zyoung staff 30M Feb 23 17:24 1000000.json
-rw-r--r-- 1 zyoung staff 314M Feb 23 17:24 10000000.json
Run Code Online (Sandbox Code Playgroud)
read,它只是加载并传递所有内容:
def read_standard(fname: str):
with open(fname) as f:
for _ in json.load(f):
pass
def read_stream(fname: str):
with open(fname) as f:
for _ in json_stream.load(f):
pass
Run Code Online (Sandbox Code Playgroud)
变换,它应用了我愚蠢的“每个奇数 BAR 都大写”:
def transform_standard(fname: str):
with open(fname) as f_in:
data = json.load(f_in)
for key, value in data.items():
if int(key) % 2 == 1:
value["foo"] = "BAR"
with open(out_name(fname), "w") as f_out:
json.dump(data, f_out, indent=1)
def transform_stream(fname: str):
@streamable_dict
def update(data):
for key, value in data.items():
value = json_stream.to_standard_types(value)
if int(key) % 2 == 1:
value["foo"] = "BAR"
yield key, value
with open(fname) as f_in:
data = json_stream.load(f_in)
updated_data = update(data)
with open(out_name(fname), "w") as f_out:
json.dump(updated_data, f_out, indent=1)
Run Code Online (Sandbox Code Playgroud)
@streamable_dict再次使用将update()迭代器转换为可传递给标准方法的可流式“事物” json.dump()。
完整的代码和运行程序都在这个 Gist中。
统计数据显示,json-stream测试 100_000、1_000_000 和 10_000_000 个对象时具有平坦的内存曲线。不过,阅读和转换确实需要更多时间:
产生
| 方法 | 项目 | 实数 | 用户 | 系统 | 内存 (MB) |
|---|---|---|---|---|---|
| 标准 | 1e+05 | 0.19 | 0.17 | 0.01 | 45.84 |
| 标准 | 1e+06 | 2.00 | 1.93 | 0.06 | 372.97 |
| 标准 | 1e+07 | 21.67 | 20.46 | 1.03 | 3480.29 |
| 溪流 | 1e+05 | 0.18 | 0.15 | 0.00 | 7.28 |
| 溪流 | 1e+06 | 1.43 | 1.41 | 0.02 | 7.69 |
| 溪流 | 1e+07 | 14.41 | 14.07 | 0.20 | 7.58 |
读
| 方法 | 项目 | 实数 | 用户 | 系统 | 内存 (MB) |
|---|---|---|---|---|---|
| 标准 | 1e+05 | 0.05 | 0.04 | 0.01 | 48.28 |
| 标准 | 1e+06 | 0.58 | 0.50 | 0.05 | 390.17 |
| 标准 | 1e+07 | 7.69 | 6.73 | 0.80 | 3875.81 |
| 溪流 | 1e+05 | 0.32 | 0.31 | 0.01 | 7.70 |
| 溪流 | 1e+06 | 2.96 | 2.94 | 0.02 | 7.69 |
| 溪流 | 1e+07 | 29.88 | 29.65 | 0.17 | 7.77 |
转换
| 方法 | 项目 | 实数 | 用户 | 系统 | 内存 (MB) |
|---|---|---|---|---|---|
| 标准 | 1e+05 | 0.19 | 0.17 | 0.01 | 48.05 |
| 标准 | 1e+06 | 1.83 | 1.75 | 0.07 | 388.83 |
| 标准 | 1e+07 | 20.16 | 19.15 | 0.91 | 3875.49 |
| 溪流 | 1e+05 | 0.63 | 0.61 | 0.01 | 7.61 |
| 溪流 | 1e+06 | 6.06 | 6.02 | 0.03 | 7.92 |
| 溪流 | 1e+07 | 61.44 | 60.89 | 0.35 | 8.44 |
| 归档时间: |
|
| 查看次数: |
10324 次 |
| 最近记录: |