Определить, совпадает ли тип / структура данных с базовым описанием данных?

Представьте себе простую метку описания данных, которая используется для описания типа данных, которыми обмениваются две системы. Чтобы обеспечить целостность данных между этими системами (которые также предназначены для расширения за счет создания новых модулей), они должны иметь простое описание типа данных и структуры, которые они выводят. Учитывая, что эти системы в основном обрабатывают команды и выходные данные командной строки, типы данных не должны быть слишком сложными.

Поскольку язык, на котором написана эта система, - это Python, простыми типами данных являются bool, int, float, str, list и dict. Списки и дикты должны быть явно определены с тем, какой тип данных они содержат (т.е. list (float) для списка 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

Очевидно, рекурсия - это метод решения этой проблемы, если бы я написал ее с нуля, и это не слишком сложная задача, но мне было интересно, есть ли уже модуль для такой функциональности.

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)
Другие вопросы по тегам