Search code examples
pythonarraysidioms

python: automatically differentiate between list / tuple / array and int / float


I have some objects which can either be floats (or integers) or tuples (or lists or arrays). Is it possible to use idiomatic Python to avoid writing an if/elif function? Right now I have

def f(attribute_a,attribute_b):
    if type(attribute_a) == float or type(attribute_a) == int:
        result = attribute_a + attribute_b
    elif type(attribute_a) == list or type(attribute_a) == tuple or type(attribute_a) == array:
        result = numpy.array([ attribute_a[i] + attribute_b[i] for i in range(len(attribute_a)) ])
    return result

I'd like to avoid the elif stuff because sometimes I want attribute_a to be a float but attribute_b to be a list in which case, I'd like something like

result = numpy.array([ attribute_a + attribute_b[i] for i in range(len(attribute_b)) ])

since I have five different attributes, it's infeasible to write out the full set of possible if loops for every possible combination of which one is a number and which is a list.

Any help is appreciated, Thanks, Sam

EDIT, 1/23/15: One idea is to define two new functions as follows

def general_value(x,i):
    if type(x)==float or type(x)==int:
        return x
    elif type(x)==list or type(x)==tuple:
        return x[i]
def general_len(x):
    if type(x)==float or type(x)==int:
        return 1
    elif type(x)==list or type(x)==tuple:
         return len(x)

(or with the various generalizations to isintance, etc.), and then plug these in where needed. Is this a reasonable hack, or is it somehow infelicitous?


Solution

  • Use isinstance where you can pass a tuple of types to avoid your multiple or's:

    if  isinstance(attribute_a,(int,float))
       ...
    elif isinstance(attribute_a,(list,tuple, array)):
    

    If you can only have either two possible cases use if/else:

      if  isinstance(attribute_a,(int,float))
         result =  ...
      else:
         result =  ...
    

    You could use a conditional expression but your statement would be rather long:

    result =  attribute_a + attribute_b if isinstance(attribute_a,(int,float)) else numpy.array([ attribute_a[i] + attribute_b[i] for i in range(len(attribute_a)) ])
    

    If you want to check two attributes for a group of possible match combinations:

    if isinstance(attribute_a,(list,tuple,float) and isinstance(attribute_b,(float,list))):
    

    Another way is to store the result of isinstance and negate the checks to avoid repeated calls:

    a,b = if isinstance(attribute_a,(list,tuple) ,isinstance(attribute_b,float))
    
    if a and b:
       ...
    elif  a and not b:
       ...
    else:
        ....
    

    If you want to check if either is a tuple,list, etc.. then you can use:

    from collections import Iterable
    def check(ele):
       return isinstance(ele,Iterable) and not isinstance(ele,basestring)
    

    Then:

    if check(attribute_a) and check(attribute_b):
        .....