开发者

Verifying that an object in python adheres to a specific structure

Is there some simple method that can check if an input object to some function adheres to a specific structure? For example, I want only a dictionary of string keys and values that are a list of integers.

One method would be to write a recursive function that you pass in the object and you iterate over it, checking at each level it is what you expect. But I feel that there should be开发者_StackOverflow中文版 a more elegant way to do it than this in python.


Why would you expect Python to provide an "elegant way" to check types, since the whole idea of type-checking is so utterly alien to the Pythonic way of conceiving the world and interacting with it?! Normally in Python you'd use duck typing -- so "an integer" might equally well be an int, a long, a gmpy.mpz -- types with no relation to each other except they all implement the same core signature... just as "a dict" might be any implementation of mapping, and so forth.

The new-in-2.6-and-later concept of "abstract base classes" provides a more systematic way to implement and verify duck typing, and 3.0-and-later function annotations let you interface with such a checking system (third-party, since Python adopts no such system for the foreseeable future). For example, this recipe provides a 3.0-and-later way to perform "kinda but not quite" type checking based on function annotations -- though I doubt it goes anywhere as deep as you desire, but then, it's early times for function annotations, and most of us Pythonistas feel so little craving for such checking that we're unlikely to run flat out to implement such monumental systems in lieu of actually useful code;-).


Short answer, no, you have to create your own function.

Long answer: its not pythonic to do what you're asking. There might be some special cases (e.g, marshalling a dict to xmlrpc), but by and large, assume the objects will act like what they're documented to be. If they don't, let the AttributeError bubble up. If you are ok with coercing values, then use str() and int() to convert them. They could, afterall, implement __str__, __add__, etc that makes them not descendants of int/str, but still usable.

def dict_of_string_and_ints(obj):
  assert isinstance(obj, dict)
  for key, value in obj.iteritems(): # py2.4
    assert isinstance(key, basestring)
    assert isinstance(value, list)
    assert sum(isinstance(x, int) for x in value) == len(list)


Since Python emphasizes things just working, your best bet is to just assert as you go and trust the users of your library to feed you proper data. Let exceptions happen, if you must; that's on your clients for not reading your docstring.

In your case, something like this:

def myfunction(arrrrrgs):
    assert issubclass(dict, type(arrrrrgs)), "Need a dictionary!"
    for key in arrrrrgs:
        assert type(key) is str, "Need a string!"
        val = arrrrrgs[key]
        assert type(val) is list, "Need a list!"

And so forth.

Really, it isn't worth the effort, and let your program blow up if you express yourself clearly in the docstring, or throw well-placed exceptions to guide the late-night debugger.


I will take a shot and propose a helper function that can do something like that for you in a more generic+elegant way:

def check_type(value, type_def):
    """
    This validates an object instanct <value> against a type template <type_def>
    presented as a simplified object.
    E.g.
    if value is list of dictionaries that have string values as key and integers 
    as values:
    >> check_type(value, [{'':0}])
    if value is list of dictionaries, no restriction on key/values
    >> check_type(value, [{}])
    """
    if type(value) != type(type_def):
        return False
    if hasattr(value, '__iter__'):
        if len(type_def) == 0:
            return True
        type_def_val = iter(type_def).next()
        for key in value:
            if not check_type(key, type_def_val):
                return False
        if type(value) is dict:
            if not check_type(value.values(), type_def.values()):
                return False
    return True

The comment explains a sample of usage, but you can always go pretty deep, e.g.

>>> check_type({1:['a', 'b'], 2:['c', 'd']}, {0:['']})
True
>>> check_type({1:['a', 'b'], 2:['c', 3]}, {0:['']})
False

P.S. Feel free to modify it if you want one-by-one tuple validation (e.g. validation against ([], '', {0:0}) which is not handled as it is expected now)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜