1

这是我的代码中经常发生的情况。假设我有一个函数do_sth(a,b),仅出于本示例的目的,只需a+b使用a,b一维numpy数组或标量进行计算。在很多情况下,我需要函数来广播操作,这样如果两者a,b都是一维数组,结果将是一个二维数组。我的意思的一个例子如下:

do_sth(1,2) -> 3
do_sth([1,2],0) -> array([1, 2])
do_sth(0,[3,4]) -> array([3, 4])
do_sth([1,2],[3,4]) -> array([[4, 5], [5, 6]])

这有点类似于 numpy 的ufunc行为方式。一个可能的实现如下:

from numpy import newaxis, atleast_1d

def do_sth(a, b):
    "a,b should be either 1d numpy arrays or scalars"
    a, b = map(atleast_1d, [a, b])
    # the line below mocks a more complicated calculation
    res = a[:, newaxis] + b[newaxis]

    conds = [a.size == 1, b.size == 1]

    if all(conds):
        return res[0, 0]
    elif any(conds):
        return res.ravel()
    else:
        return res

如您所见,有很多样板。第一个问题是:这是进行这种输入/输出转换的正确方法吗?有没有理由不使用装饰器来处理这种情况?有没有关于此事的指导方针?

此外,更复杂的计算,在这里被加法模拟,如果a或者b是具有 2D、3D 形状的 numpy 数组,通常会严重失败。从某种意义上说,计算失败的点并不明显,或者在代码的不同版本中可能会随时间变化,并且很难看出错误与错误输入形状之间的联系。我认为不建议将复杂的计算放在一个try/except块中(遵循python EAFP)。在这种情况下,在函数开头检查 2 个数组的形状是否正确?有没有其他选择?是否有一个 numpy 函数允许同时将输入转换为 numpy 数组,并检查输入是否与一定数量的维度兼容,例如asarray_withdim(arr,ndim=5)

4

1 回答 1

1

关于装饰器的使用——我在numpy代码中没有看到太多装饰器的使用,但我认为这是因为大部分功能是在装饰器在 Python 中变得普遍之前开发的。如果你能让它工作,就不应该有任何缺点(但我不是装饰器或装饰器的专家ufunc)。

未编译的 numpy 函数通常有很多代码将输入处理成方便的维度。然后他们执行核心操作,然后是最终的整形和类型包装。他们可能会使用 np.atleast_2d 之类的函数来确保有足够的维度,并使用 .reshape(-1,1,1) 来压缩多余的维度。

np.tensordot是对输入执行轴转置和整形的示例,因此它可以应用已编译的np.dot. np.insert从一些ndimisinstance测试开始。特殊情况及早处理,一般情况留到最后。 np.einsum已编译,但在最终创建nditer对象并进行计算之前,在 C 代码中进行了大量预处理。

于 2014-12-08T01:03:30.803 回答