Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Duck typing trouble. Duck typing test for "i-am-like-a-list"

Tags:

python

list

USAGE CONTEXT ADDED AT END

I often want to operate on an abstract object like a list. e.g.

def list_ish(thing):
    for i in xrange(0,len(thing)):
        print thing[i]

Now this appropriate if thing is a list, but will fail if thing is a dict for example. what is the pythonic why to ask "do you behave like a list?"

NOTE:

hasattr('__getitem__') and not hasattr('keys')

this will work for all cases I can think of, but I don't like defining a duck type negatively, as I expect there could be cases that it does not catch.

really what I want is to ask.
"hey do you operate on integer indicies in the way I expect a list to do?" e.g.

  thing[i],  thing[4:7] = [...],   etc.

NOTE: I do not want to simply execute my operations inside of a large try/except, since they are destructive. it is not cool to try and fail here....

USAGE CONTEXT -- A "point-lists" is a list-like-thing that contains dict-like-things as its elements. -- A "matrix" is a list-like-thing that contains list-like-things

-- I have a library of functions that operate on point-lists and also in an analogous way on matrix like things.

-- for example, From the users point of view destructive operations like the "spreadsheet-like" operations "column-slice" can operate on both matrix objects and also on point-list objects in an analogous way -- the resulting thing is like the original one, but only has the specified columns.

-- since this particular operation is destructive it would not be cool to proceed as if an object were a matrix, only to find out part way thru the operation, it was really a point-list or none-of-the-above.

-- I want my 'is_matrix' and 'is_point_list' tests to be performant, since they sometimes occur inside inner loops. So I would be satisfied with a test which only investigated element zero for example.

-- I would prefer tests that do not involve construction of temporary objects, just to determine an object's type, but maybe that is not the python way.

in general I find the whole duck typing thing to be kinda messy, and fraught with bugs and slowness, but maybe I dont yet think like a true Pythonista

happy to drink more kool-aid...

like image 795
Dan Oblinger Avatar asked May 20 '15 00:05

Dan Oblinger


1 Answers

One thing you can do, that should work quickly on a normal list and fail on a normal dict, is taking a zero-length slice from the front:

try:
    thing[:0]
except TypeError:
    # probably not list-like
else:
    # probably list-like

The slice fails on dicts because slices are not hashable.

However, str and unicode also pass this test, and you mention that you are doing destructive edits. That means you probably also want to check for __delitem__ and __setitem__:

def supports_slices_and_editing(thing):
    if hasattr(thing, '__setitem__') and hasattr(thing, '__delitem__'):
        try:
            thing[:0]
            return True
        except TypeError:
            pass
    return False

I suggest you organize the requirements you have for your input, and the range of possible inputs you want your function to handle, more explicitly than you have so far in your question. If you really just wanted to handle lists and dicts, you'd be using isinstance, right? Maybe what your method does could only ever delete items, or only ever replace items, so you don't need to check for the other capability. Document these requirements for future reference.

like image 188
Dan Getz Avatar answered Nov 01 '22 18:11

Dan Getz