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



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

因为这个系统是用Python编写的语言,所以简单的数据类型是bool,int,float,str,list和dict。 列表和字典必须使用它们包含的数据类型显式定义(即 list(float) 表示浮点数列表或 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
显然,如果我从头开始

编写递归是解决此问题的方法,并且这不是一个太困难的问题,但我想知道是否已经有一个用于此类功能的模块。

这就是我到目前为止所拥有的,它之前被更好地打包到"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)

最新更新