你好,我是忆愿,全网4w+粉丝,《遂愿盈创》社群主理人。
副业启航① | 遂愿盈创(对副业感兴趣免费可入,多种赚钱实战项目等你来,一起探寻副业快速变现的途径;以及对接互联网大厂商务合作,一起来搞点小外快,认识更多互联网大咖)
目前群里已经带很多小伙伴(大部分大学生)变现几百块啦,程序员搞副业有额外加成~ 对副业感兴趣可+V : suiyuan2ying 拉你进群。
处理 JSON 数据是每个程序员的日常,Python 标准库里的 json 模块肯定都用过。
不过你肯定遇到过解析特别大的 JSON 文件时卡得要死,或者处理某些特殊格式时直接报错的情况吧?
今天咱们就来聊聊这个叫 simplejson 的第三方库,它不光性能杠杠的,功能还特别强大。
说真的,用了这个库之后,我都不想用标准库的 json 模块了。
都说一个好汉三个帮,Python 处理 JSON 也不是非得用标准库不可。
simplejson 最早是 Python 标准库 json 模块的前身,后来独立发展,现在已经成为了一个功能更强大、性能更好的替代品。
安装特别简单:
pip install simplejson
基础用法和标准库几乎一模一样:
import simplejson as json
# 把 Python 对象转成 JSON 字符串
data = {'name': '忆愿', 'age': 18}
json_str = json.dumps(data)
# 把 JSON 字符串解析成 Python 对象
data = json.loads(json_str)
温馨提示:建议用 import simplejson as json
这种方式导入,这样以后想换回标准库的时候,代码都不用改。
光说不练假把式,咱们来实际测试下 simplejson 和标准库的性能差异:
import json as stdlib_json
import simplejson as json
import time
import random
# 准备测试数据
def generate_test_data(size):
return [{
'id': i,
'name': f'test_{i}',
'value': random.random(),
'items': [random.randint(1, 100) for _ in range(10)]
} for i in range(size)]
# 性能测试函数
def performance_test(data, rounds=5):
results = {'stdlib': [], 'simplejson': []}
for _ in range(rounds):
# 测试标准库
start = time.time()
stdlib_str = stdlib_json.dumps(data)
stdlib_obj = stdlib_json.loads(stdlib_str)
results['stdlib'].append(time.time() - start)
# 测试 simplejson
start = time.time()
json_str = json.dumps(data)
json_obj = json.loads(json_str)
results['simplejson'].append(time.time() - start)
return results
# 执行测试
test_data = generate_test_data(10000)
results = performance_test(test_data)
print("标准库平均耗时:", sum(results['stdlib']) / len(results['stdlib']))
print("simplejson平均耗时:", sum(results['simplejson']) / len(results['simplejson']))
在我的电脑上,处理同样的数据,simplejson 平均比标准库快 20-30%。
这还只是小数据量,数据量越大,差距越明显。
说实话,标准库在处理特殊数据类型时就显得有点力不从心了。
比如 Decimal、datetime、自定义对象这些,经常会遇到各种报错。simplejson 在这方面就强大多了。
from decimal import Decimal
import simplejson as json
# 价格数据,精度很重要
data = {
'products': [
{'name': '手机', 'price': Decimal('1999.99')},
{'name': '耳机', 'price': Decimal('299.50')},
],
'total': Decimal('2299.49')
}
# simplejson 可以直接处理 Decimal
json_str = json.dumps(data, use_decimal=True)
print(json_str)
# 解析时自动转回 Decimal
parsed_data = json.loads(json_str, use_decimal=True)
print(type(parsed_data['total'])) #
from datetime import datetime, date
import simplejson as json
class DateTimeEncoder:
def default(self, obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
return obj
data = {
'created_at': datetime.now(),
'delivery_date': date(2024, 3, 15)
}
# 使用自定义编码器
json_str = json.dumps(data, default=DateTimeEncoder().default)
print(json_str)
温馨提示:处理时间类型时,最好统一用 ISO 格式,这样方便前后端交互。
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def to_json(self):
return {
'name': self.name,
'age': self.age
}
class CustomEncoder:
def default(self, obj):
if hasattr(obj, 'to_json'):
return obj.to_json()
return obj
user = User('忆愿', 18)
json_str = json.dumps(user, default=CustomEncoder().default)
print(json_str) # {"name": "忆愿", "age": 18}
处理超大 JSON 文件时,一次性读入内存可能会爆掉。simplejson 提供了增量解析的功能:
def process_huge_json(filename):
chunk_size = 8192 # 每次读取 8KB
parser = json.JSONParser()
with open(filename, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
parser.feed(chunk)
return parser.close()
# 使用示例
try:
data = process_huge_json('huge_file.json')
print("解析成功!")
except json.JSONDecodeError as e:
print(f"解析出错:{e}")
simplejson 提供了很多可以调优性能的选项:
data = generate_test_data(100000)
# 1. 关闭循环引用检查
json_str = json.dumps(data, check_circular=False)
# 2. 使用最紧凑的输出
json_str = json.dumps(data, separators=(',', ':'))
# 3. 跳过编码检查
json_str = json.dumps(data, ensure_ascii=False)
# 4. 组合使用所有优化选项
json_str = json.dumps(data,
check_circular=False,
separators=(',', ':'),
ensure_ascii=False
)
simplejson 的错误处理也比标准库强大:
def safe_json_loads(json_str):
try:
return json.loads(json_str,
# 允许控制字符
strict=False,
# 允许重复的键
object_pairs_hook=None
)
except json.JSONDecodeError as e:
print(f"解析错误:{str(e)}")
print(f"错误位置:行 {e.lineno}, 列 {e.colno}")
print(f"错误文档片段:{e.doc[e.pos-20:e.pos+20]}")
return None
# 测试各种异常情况
bad_json = '''
{
"name": "测试",
"desc": "包含控制字符\x00",
"duplicate": 1,
"duplicate": 2
}
'''
result = safe_json_loads(bad_json)
import requests
import simplejson as json
class APIClient:
def __init__(self, base_url):
self.base_url = base_url
def get_data(self, endpoint):
response = requests.get(f"{self.base_url}{endpoint}")
try:
# 使用 simplejson 解析响应
data = json.loads(
response.text,
use_decimal=True, # 精确处理数字
ignore_nan=True # 忽略 NaN
)
return data
except json.JSONDecodeError as e:
print(f"API响应解析失败:{e}")
return None
# 使用示例
api = APIClient('https://api.example.com')
data = api.get_data('/products')
class Config:
def __init__(self, config_file):
self.config_file = config_file
self.config = self._load_config()
def _load_config(self):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
return json.load(f, use_decimal=True)
except (json.JSONDecodeError, FileNotFoundError) as e:
print(f"配置文件加载失败:{e}")
return {}
def save_config(self):
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(self.config, f,
indent=4,
ensure_ascii=False,
use_decimal=True
)
return True
except Exception as e:
print(f"配置保存失败:{e}")
return False
def get(self, key, default=None):
return self.config.get(key, default)
def set(self, key, value):
self.config[key] = value
# 使用示例
config = Config('app_config.json')
config.set('debug_mode', True)
config.set('max_connections', 100)
config.save_config()
class DataExporter:
def __init__(self, chunk_size=1000):
self.chunk_size = chunk_size
def export_to_json(self, data, output_file):
try:
# 分块写入大数据
with open(output_file, 'w', encoding='utf-8') as f:
f.write('[\n') # 开始数组
for i, item in enumerate(data):
json_str = json.dumps(
item,
ensure_ascii=False,
use_decimal=True
)
if i > 0:
f.write(',\n')
f.write(json_str)
# 定期刷新缓冲区
if i % self.chunk_size == 0:
f.flush()
f.write('\n]') # 结束数组
return True
except Exception as e:
print(f"数据导出失败:{e}")
return False
# 使用示例
exporter = DataExporter()
data = generate_test_data(10000)
exporter.export_to_json(data, 'export.json')
在实际项目中,JSON 处理的性能往往是个关键问题。
这里分享几个实用的优化技巧:
处理超大 JSON 文件时,可以使用内存映射:
import mmap
import os
def process_large_json(filename):
with open(filename, 'rb') as f:
# 创建内存映射
with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
# 使用 simplejson 解析
return json.loads(mm)
from concurrent.futures import ProcessPoolExecutor
import math
def parallel_json_process(data, worker_count=None):
if worker_count is None:
worker_count = os.cpu_count()
# 数据分片
chunk_size = math.ceil(len(data) / worker_count)
chunks = [data[i:i + chunk_size] for i in range(0, len(data), chunk_size)]
results = []
with ProcessPoolExecutor(max_workers=worker_count) as executor:
# 并行处理每个分片
future_to_chunk = {
executor.submit(json.dumps, chunk): i
for i, chunk in enumerate(chunks)
}
for future in future_to_chunk:
try:
results.append(future.result())
except Exception as e:
print(f"处理分片 {future_to_chunk[future]} 失败:{e}")
return results
对于频繁访问的 JSON 数据,可以使用缓存:
from functools import lru_cache
import hashlib
class JSONCache:
def __init__(self, maxsize=128):
self.cache = lru_cache(maxsize=maxsize)(self._process_json)
def _get_cache_key(self, json_str):
# 使用 MD5 作为缓存键
return hashlib.md5(json_str.encode()).hexdigest()
def _process_json(self, cache_key):
# 实际的 JSON 处理逻辑
return json.loads(self._cache_data[cache_key])
def process(self, json_str):
self._cache_data = {self._get_cache_key(json_str): json_str}
return self.cache(self._get_cache_key(json_str))
# 使用示例
cache = JSONCache()
result1 = cache.process('{"name": "test"}')
result2 = cache.process('{"name": "test"}') # 直接从缓存返回
写了这么多,不知不觉就写了很多干货。simplejson 真的是个特别强大的库,它不仅解决了标准库的很多限制,还提供了很多实用的功能。
特别是在处理大规模数据、特殊类型数据时,simplejson 的优势就特别明显。
平时写代码遇到 JSON 相关的需求,我现在基本都是直接上 simplejson。
它的性能好、功能强、使用简单,还兼容标准库的接口,简直就是 Python 处理 JSON 数据的完美解决方案。
对了,记得在处理 JSON 数据时要注意异常处理,毕竟数据格式千奇百怪,多加个 try-except 总是没错的。
还有就是处理大文件时,要考虑内存使用,可以用增量解析或者分块处理的方式。