验证是json还是python字典的pythonic方法



我需要验证'info'是json文件还是python字典。鉴于 json 文件和 python 字典具有相同的结构,我编写了这段代码来解析两者并将其内容保存在变量中,但我认为有一个 pythonic 和更有效的代码。

import json
class LoadInfo(object):
def __init__(self, info=None):
if info:
try:
self.config= json.loads(info)
except ValueError:
print('Load Python Dict')
try:
if isinstance(info, dict):
self.config= info
except ValueError:
print('Json or python dict config is needed')
else:
raise Exception('Json or python dict config is needed')
info  = LoadInfo('path/to/json_file') #should work
info_dict = dict('A'=1, 'B'=2, 'C'=3)
info2 = LoadInfo(info_dict) #Also should work

有人有更多的思想吗?

首先,不要只raise Exception; 这太笼统了,尽可能具体地说明出了什么问题。在这种情况下,用户尚未提供info参数。这样做有两个问题:

  1. 您应该通过身份而不是真实性来测试None(否则例如{}将是一个例外,这可能不是您真正想要的(:if info is not None:.

  2. 如果它是必需参数,为什么要给它一个默认值?!

修订版 1:

import json
class LoadInfo(object):
def __init__(self, info):
try:
self.config = json.loads(info)
except ValueError:
print('Load Python Dict')
try:
if isinstance(info, dict):
self.config = info
except ValueError:
print('python dict config is needed')

(请注意细微的风格指南调整。


接下来,实际上没有必要通过允许字典或 JSON 字符串作为参数来提供这种多态性。与第二种情况一样,您只需将其解析为第一种情况,将其设置为类方法,这是 Python 中常见的替代构造函数模式。

修订版 2:

import json
class LoadInfo(object):
def __init__(self, info):
try:
if isinstance(info, dict):
self.config = info
except ValueError:
print('python dict config is needed')
@classmethod
def from_json(cls, info):
return cls(json.loads(info))

哪一部分:

if isinstance(info, dict):
self.config = info

你希望提高ValueError吗?为什么,在它不是一种可接受的输入类型的情况下,你只想print一些东西,让程序继续?请注意,在检查类型时,最好使用 ABC。

修订版 3:

from collections.abc import Mapping
import json
class LoadInfo(object):
def __init__(self, info):
if not isinstance(info, Mapping):
raise TypeError('mapping config is needed')
self.config = info
@classmethod
def from_json(cls, info):
return cls(json.loads(info))

但实际上,您建议从文件加载它,而不是当前代码所暗示的 JSON 字符串(您提供的'path/to/json_file'不是'{"foo": "bar"}'- 不清楚您希望json.loads对此做什么(。所以你需要处理那个文件。

修订版 4:

from collections.abc import Mapping
import json
class LoadInfo(object):
def __init__(self, info):
if not isinstance(info, Mapping):
raise TypeError('mapping config is needed')
self.config = info
@classmethod
def from_json_file(cls, filename):
with open(filename) as json_file:
return cls(json.load(json_file))  # note 'load' not 'loads'

现在您的示例变为:

info = LoadInfo.from_json_file('path/to/json_file')
info_dict = dict(A=1, B=2, C=3)  # note you shouldn't use quotes for keys here
info2 = LoadInfo(info_dict)

如果你传递一个文件,你需要首先打开一个文件对象,最好将文件和字符串参数分开:

import os
import json
class LoadInfo(object):
def __init__(self, info=None, file=None):
if file and os.path.exists(file):
with open(file) as f:
data = f.read()
try:
self.config = json.loads(data)
except ValueError:
raise ValueError('Load JSON file error')
elif info:
if isinstance(info, dict):
self.config = info
elif isinstance(info, str):
try:
self.config = json.loads(info)
except ValueError:
raise ValueError('Load JSON string error')
else:
raise ValueError('Load config error')

我会把它分成两种方法:

class LoadInfo(object):
def load_from_file(self, file):
with open(file) as f:
data = f.read()
self.config = json.loads(data)
def load_from_str(self, info):
if isinstance(info, dict):
self.config = info
elif isinstance(info, str):
self.config = json.loads(info)
else:
raise ValueError('Load config error')

但实际上,使用鸭子打字风格更蟒蛇。

最新更新