Python:复杂返回值的类型检查

时间:2013-11-30 16:31:44

标签: python typechecking

我正在编写一个框架,调用其他人编写的代码(该框架扮演Monopoly并调用玩家AI)。 AI告诉框架在函数调用的返回值中要做什么。

我想检查返回值的类型,以确保它们不会破坏我的框架代码。

例如:

instructions = player.sell_houses()
# Process the instructions...

在这个例子中,我期待玩家返回一个元组列表,例如:

[(Square.BOW_STREET, 2), (Square.MARLBOROUGH_STREET, 2), (Square.VINE_STREET, 1)]

是否有简化(ish)方法来验证AI返回给我的内容?我想象的是这样的事情:

    instructions = player.sell_houses()
    if not typecheck(instructions, [(str, int)]):
        # Data was not valid...

我不只是想检查返回的数据是否为列表。我想检查它是否是特定类型的列表。在示例中,它是一个元组列表,其中每个元组包含一个字符串和一个整数。

我已经看到很多Python类型检查问题得到了答案“类型检查是邪恶的”。如果是这样,在这种情况下我该怎么做?似乎没有什么能阻止AI代码返回任何东西,我必须能够以某种方式验证或处理它。


编辑:我可以通过写一个函数来“手动”检查这个。对于上面的instructions,它可能是这样的:

def is_valid(instructions):
    if not isinstance(instructions, list): return False
    for item in instructions:
        if not isinstance(item, tuple): return False
        if len(item) != 2: return False
        if not isinstance(item[0], str): return False
        if not isinstance(item[1], int): return False
    return True

但在这种情况下,我必须为我需要验证的每种类型的值编写一个类似的复杂验证函数。所以我想知道是否存在一个更通用的验证函数或库,我可以给它一个表达式(如[(str, int)]),它将验证它而不需要手工完成工作。

3 个答案:

答案 0 :(得分:2)

很抱歉回答我自己的问题。从目前为止的答案来看,可能没有库函数可以做到这一点,所以我写了一个:

def is_iterable(object):
    '''
    Returns True if the object is iterable, False if it is not.
    '''
    try:
        i = iter(object)
    except TypeError:
        return False
    else:
        return True


def validate_type(object, type_or_prototype):
    '''
    Returns True if the object is of the type passed in.

    The type can be a straightforward type, such as int, list, or
    a class type. If so, we check that the object is an instance of
    the type.

    Alternatively the 'type' can be a prototype instance of a more
    complex type. For example:
    [int]                   a list of ints
    [(str, int)]            a list of (str, int) tuples
    {str: [(float, float)]} a dictionary of strings to lists of (float, float) tuples

    In these cases we recursively check the sub-items to see if they match
    the prototype.
    '''
    # If the type_or_prototype is a type, we can check directly against it...
    type_of_type = type(type_or_prototype)
    if type_of_type == type:
        return isinstance(object, type_or_prototype)

    # We have a prototype.

    # We check that the object is of the right type...
    if not isinstance(object, type_of_type):
        return False

    # We check each sub-item in object to see if it is of the right sub-type...
    if(isinstance(object, dict)):
        # The object is a dictionary, so we check that its items match
        # the prototype...
        prototype = type_or_prototype.popitem()
        for sub_item in object.items():
            if not validate_type(sub_item, prototype):
                return False

    elif(isinstance(object, tuple)):
        # For tuples, we check that each element of the tuple is
        # of the same type as each element the prototype...
        if len(object) != len(type_or_prototype):
            return False
        for i in range(len(object)):
            if not validate_type(object[i], type_or_prototype[i]):
                return False

    elif is_iterable(object):
        # The object is a non-dictionary collection such as a list or set.
        # For these, we check that all items in the object match the
        prototype = iter(type_or_prototype).__next__()
        for sub_item in object:
            if not validate_type(sub_item, prototype):
                return False

    else:
        # We don't know how to check this object...
        raise Exception("Can not validate this object")

    return True

您可以像isinstance一样使用简单类型,例如:

validate_type(3.4, float)
Out[1]: True

或者使用更复杂的嵌入式类型:

list1 = [("hello", 2), ("world", 3)]
validate_type(list1, [(str, int)])
Out[2]: True

答案 1 :(得分:1)

我认为你正在寻找isinstance

isinstance(instruction, (str, int))

如果指令是Truestr的实例,则返回int

要全部检查,您可以使用all()功能:

all(isinstance(e[0], (str, int)) for e in instructions)

这将检查元组的所有第一个元素是str还是int的实例。如果其中任何一个无效,则返回False

答案 2 :(得分:1)

您可以使用type功能:

>>> type("abc") in [str, tuple]
True

虽然aIKid的答案会做同样的事情。