如何编写一个可以接受浮点数、列表或numpy数组的函数?

11

我有一个简单的 Python 函数:

def get_lerp_factor( a, x, b ):
    if x <= a: return 0.
    if x >= b: return 1.
    return (x - a) / (b - a)

许多NumPy函数,比如numpy.sin(x),可以处理浮点数或数组。

那么我该如何以同样的方式扩展它,使其也可以处理x为NumPy数组的情况?

def get_lerp_factor( a, x_maybe_array, b ):
    out = (x_maybe_array - a) / (b - a) # this should work...
    # but now I have to clamp each element of out between 0 and 1

我需要检查x的类型,并作出相应的处理吗?

那么这样怎么样:

def get_lerp_factor( a, x_anything, b ):
    x = np.array( x_anything )
    out = ...(x)
    # now typecast out back into the same type as x... will this work?

?


在我看来,这不是一个很好的想法,因为它违反了“单一职责原则”。 - Paulo Bu
当给出一个列表时,您希望它如何表现? - SylvainD
@PauloBu:大多数NumPy函数都是这样运作的(接受任何可以转换为数组的参数),因此单一职责原则早已被抛弃。 - Gareth Rees
@Josay:它应该返回一个包含逐元素lerp因子的NumPy数组。 - Gareth Rees
@msvalkon:与NumPy数组操作相比,map和列表推导式速度较慢,因此这是不可取的。 - Gareth Rees
显示剩余2条评论
2个回答

19

您需要使用numpy.asarray。它以以下方式作为其第一个参数:

输入数据,以任何可转换为数组的形式。这包括列表、元组列表、元组、元组列表、列表的元组和ndarrays。

并且它返回:

a的数组表示。如果输入已经是ndarray,则不执行复制操作。

因此您可以像这样实现您的函数:

import numpy as np

def get_lerp_factor(a, x, b):
    a, x, b = np.asarray(a), np.asarray(x), np.asarray(b)
    return ((x - a) / (b - a)).clip(0, 1)

这适用于标量:

>>> get_lerp_factor(0, 9, 16)
0.5625

还适用于可迭代对象:

>>> get_lerp_factor(2, range(8), 6)
array([ 0.  ,  0.  ,  0.  ,  0.25,  0.5 ,  0.75,  1.  ,  1.  ])

2
实际上,只要numpy数组具有您使用的运算符(-/<=>=)所需的语义,它就已经可以工作了。这被称为“鸭子类型”,其中您不关心参数的类型,只关心它们以特定方式行事。
当然,列表不会表现出这样的行为。而numpy数组可能也不完全像那样表现(<= 运算符有效,但结果是一个布尔数组,=> 运算符未定义)。因此,您需要在运行时检查类型。一种方法是检查是否支持__len__方法:
try:
    if len(a) == len(b) == len(x):
         # handle list / array case
except TypeError:
    # oops, was a float

请注意,显式检查类型(使用isinstance(o, t))通常是一个非常糟糕的想法,因为您希望尽可能地保持鸭子类型语义。但有时您需要这样做。
还要注意,仅当函数的“语义”不受输入类型影响时,这才是可接受的。如果您根据输入类型更改函数的含义,则会遇到麻烦!更糟糕的是:您的用户(函数的消费者)也会遇到麻烦。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接