用于验证是否为json或python dict的Pythonic方法

时间:2017-07-10 21:20:00

标签: python json

我需要验证'info'是json文件还是python dict。鉴于json文件和python dict具有相同的结构,我编写了这个代码来解析它们并将它们的内容保存在变量中,但我认为有一个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

任何人都有更多的想法?

2 个答案:

答案 0 :(得分:4)

首先,不要只是raise Exception;这太普遍了,对于出了什么问题,尽可能具体。在这种情况下,用户没有提供info参数。两个问题:

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

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

  3. 修订版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')
    

    (注意次要style guide调整。)

    接下来,通过允许字典或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一些东西并让程序继续下去吗?请注意,在您检查类型的位置,最好使用ABCs

    修订版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)
    

答案 1 :(得分:-1)

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

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')

但实际上,使用ducktyping风格更加pythonic。