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



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

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

例如:

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

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

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

有没有一种简单的方法来验证人工智能给我的回报?我在想象这样的事情:

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

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

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


编辑:我可以通过写一个函数来"手动"检查这个。对于上面的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)]),它将根据它进行验证,而不必手工完成工作。

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

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

您可以使用type函数:

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

尽管aKid的回答也会起到同样的作用。

我想您正在寻找isinstance:

isinstance(instruction, (str, int))

如果指令是strint的实例,则返回True

要检查所有内容,可以使用all()功能:

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

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

最新更新