确定数据类型/结构是否与数据的基本描述相同?

时间:2016-03-07 22:57:42

标签: python types structure verify

想象一个简单的数据描述标签,用于描述两个系统之间交换的数据类型。为了在这些系统之间具有数据完整性(这些系统也可以通过创建新模块来扩展),它们必须对它们输出的数据类型和结构进行简单描述。鉴于这些系统主要处理命令行命令和输出,数据类型不必太复杂。

因为这个系统编写的语言是Python,所以简单的数据类型是bool,int,float,str,list和dict。列表和dicts必须使用它们包含的数据类型显式定义(即列表(浮点数)用于浮点数列表或dict(str,list(str))用于将字符串映射到字符串列表的字典。

当系统从另一个系统传递数据时,它会检查传递的数据是否遵循模块为其输入的数据描述,如果确实如此,则继续执行抛出和错误。以下几个小例子:

verify("int", 1)                            -> True
verify("int", "1")                          -> False
verify("list(int)", [1, 2, 3])              -> True
verify("list(int)", [])                     -> True
verify("dict(str,int)", {"a": 1})           -> True
verify("dict(str,int)", {"b": 1, "c": "d"}) -> False

显然,递归是解决这个问题的方法,如果我从头开始编写它并不是一个问题不太困难,但我想知道是否已经存在这种功能的模块。 / p>

1 个答案:

答案 0 :(得分:1)

这就是我到目前为止所做的,它更好地打包成了一个" TypeLabel"之前的对象,但我将其压缩成一个函数,以更紧密地跟随在答案中使用测试用例指定的函数。该函数传递了上面几个简单的测试用例,但没有提供错误检查,例如,不正确的数据类型规范。我今天晚些时候会制作一个更强大的版本。并不是说这是批评的地方,但是如果你看到任何明显的错误,请随意指出它们。

def verify(specification, test_data):
    typenames = {"tuple": tuple, "dict": dict, "list": list, "str": str, "int": int, "bool": bool, "float": float}

    def interpret_spec(spec):
        def find_separators(spec):
            seps = []
            depth = 0
            for i in range(len(spec)):
                if spec[i] == ',' and depth == 0:
                    seps.append(i)
                elif spec[i] == '(':
                    depth += 1
                elif spec[i] == ')':
                    depth -= 1
            return seps

        def recurse_type(spec):
            seps = find_separators(spec)
            if len(seps) != 0:
                sub_specs = [""]
                for i in range(len(spec)):
                    if i in seps:
                        sub_specs.append("")
                    else:
                        sub_specs[-1] += spec[i]
                if spec[-1] == "":
                    spec = spec[:-1]
                return tuple([recurse_type(sub_spec) for sub_spec in sub_specs])
            spec_name = spec
            if "(" in spec:
                spec_name = spec[:spec.find("(")]
                sub_spec = spec[spec.find("(")+1:spec.rfind(")")]
                return {spec_name: recurse_type(sub_spec)}
            else:
                return spec_name

        return recurse_type(spec.replace(" ", "").strip())

    def recurse_verify(spec, data):
        try:
            if isinstance(spec, str):
                return isinstance(data, typenames[spec])
            elif isinstance(spec, dict):
                datatype_name = spec.keys()[0]
                if not isinstance(data, typenames[datatype_name]):
                    return False
                if datatype_name == "list":
                    for item in data:
                        if not recurse_verify(spec[datatype_name], item):
                            return False
                elif datatype_name == "dict":
                    for key in data:
                        if not recurse_verify(spec[datatype_name][0], key) or not recurse_verify(spec[datatype_name][1], data[key]):
                            return False
                elif datatype_name == "tuple":
                    if len(spec[datatype_name]) != len(data):
                        return False
                    for i in range(len(data)):
                        subtype = spec[datatype_name][i]
                        subdata = data[i]
                        if not recurse_verify(subtype, subdata):
                            return False
        except TypeError:
            return False
        else:
            return True

    return recurse_verify(interpret_spec(specification), test_data)