python:自动区分list / tuple / array和int / float

时间:2015-01-23 17:29:55

标签: python arrays idiomatic

我有一些对象可以是浮点数(或整数)或元组(或列表或数组)。是否可以使用惯用Python来避免编写if / elif函数?现在我有

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

我想避免使用elif的东西,因为有时候我希望attribute_a成为一个浮点数,但是attribute_b是一个列表,在这种情况下,我喜欢像

这样的东西
result = numpy.array([ attribute_a + attribute_b[i] for i in range(len(attribute_b)) ])

因为我有五个不同的属性,所以为每个可能的组合写出完整的可能if循环是不可行的,其中一个是数字,而且是一个列表。

感谢任何帮助, 谢谢, 萨姆

EDIT,1/23/15:一个想法是定义两个新功能如下

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)

(或对isintance的各种概括等),然后将它们插入到需要的地方。这是一个合理的黑客攻击,还是某种不恰当的行为?

2 个答案:

答案 0 :(得分:4)

使用isinstance可以传递一个类型元组以避免多个or's

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

如果你只能有两种可能的情况,请使用if / else:

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

您可以使用条件表达式,但您的语句会很长:

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 isinstance(attribute_a,(list,tuple,float) and isinstance(attribute_b,(float,list))):

另一种方法是存储isinstance的结果并取消检查以避免重复调用:

a,b = if isinstance(attribute_a,(list,tuple) ,isinstance(attribute_b,float))

if a and b:
   ...
elif  a and not b:
   ...
else:
    ....

如果你想检查是否是元组,列表等,那么你可以使用:

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

然后:

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

答案 1 :(得分:3)

您可能需要考虑使用抽象基类来减少组合的数量。

已经有一个abc来测试你的一个论点是int还是float;它是numbers.Real

assert isinstance(1,numbers.Real)
assert isinstance(1.1,numbers.Real)

还有一个abc可以测试你的一个参数是listtuple还是array。它是collections.abc.Container

assert isinstance((1,),collections.abc.Container)
assert isinstance([],collections.abc.Container)
assert isinstance(array([]),collections.abc.Container)

因此功能变为:

def f(attribute_a,attribute_b):
    if isinstance(attribute_a,numbers.Real) and isinstance(attribute_b,numbers.Real):
        #Both arguments are Real numbers
        result = attribute_a + attribute_b
    elif isinstance(attribute_a,collections.abc.Container) and isinstance(attribute_b,collections.abc.Container):
        #Both arguments are Containers
        result = numpy.array([ attribute_a[i] + attribute_b[i] for i in range(len(attribute_a)) ])
    elif isinstance(attribute_a,numbers.Real) and isinstance(attribute_b,collections.abc.Container):
        # First argument is a Real number, second is a Container
        result = numpy.array([ attribute_a + attribute_b[i] for i in range(len(attribute_b)) ])
    elif isinstance(attribute_a,collections.abc.Container) and isinstance(attribute_b,numbers.Real):
        # First argument is a Container, second is a Real number
        result = numpy.array([ attribute_a[i] + attribute_b for i in range(len(attribute_a)) ])
    return result

Padraic Cunningham's answer中使用抽象基类而不是isinstance(var,(list, tuple, array))有一个重要的潜在优势:对abc进行检查将允许您的代码即使对于" act的类型也能工作喜欢"这些类型的容器,但不一定是它们的子类。同样适用于"表现为"的课程也是如此。 Real,但不一定是intfloat的子类。根据您的使用情况,这也可能是一个缺点(例如,请参阅下面有关str对象的说明)。

编辑:以下评论中的重要问题是:

assert isinstance("some string",collections.abc.Container)

如果从可迭代类型中限制字符串很重要(看起来可能如此),则可以创建自定义抽象基类using the abc module

或者,您只需添加and not isinstance(var,str)(仅限Python 3)。


编辑:不幸的是,这个答案的overload部分因为他的<3.0而无法为OP工作,但也许它会帮助其他人。

这个问题也让我想起overload library,我有意尝试这个问题。

使用该模块,尝试使用抽象基类解决您的问题:

from overload import overload
from collections.abc import Container
from numbers import Real

@overload
def f(attribute_a:Container,attribute_b:Container):
    '''Both arguments are Containers, e.g. list, tuple, or array'''
    return numpy.array([ attribute_a[i] + attribute_b[i] for i in range(len(attribute_a)) ])

@f.add
def f(attribute_a:Real,attribute_b:Real):
    '''Both arguments are Real numbers, e.g. int, float'''
    attribute_a + attribute_b

@f.add
def f(attribute_a:Real,attribute_b:Container):
    '''First argument is a Real number, second is a Container'''
    return numpy.array([ attribute_a + attribute_b[i] for i in range(len(attribute_b)) ])

@f.add
def f(attribute_a:Container,attribute_b:Real):
    '''First argument is a Container, second is a Real number'''
    return f(attribute_b,attribute_a)