确保数组参数尺寸正确的Python 3方法是什么?

时间:2018-11-12 19:01:12

标签: python arrays numpy typehints

在我的新手Python 3.7项目中,许多函数中的参数是numpy.ndarray。这些必须是二维r x n矩阵。行维度r是必不可少的:某些函数需要1 x n向量,另一些函数需要2 x n矩阵,其中r最多三个,甚至可能更多。还为任何r x n数组定义了函数。 (列尺寸n对于设计而言不是必需的。)

根据我在Matlab上的经验,此要求可能会引起混乱并且容易出错。因此,我考虑了以下方法:

  1. 记录方法参数(当然!)
  2. 单元测试(当然!)
  3. 进行验证并在某些函数中引发异常。 (但是,这不是很实用,也没有表现。)
  4. 定义数据类:OneRowTwoRowsThreeRowsFourPlusRows。每个都有一个ndarray字段,该字段在构造函数中已验证。好处包括类型提示和更好的域建模(如DDD)。缺点是额外的复杂性。

问题:考虑到Python 3中引入的类型提示以及函数式编程的趋势,解决该问题的当前pythonic方法是什么?

1 个答案:

答案 0 :(得分:2)

关于Python的最好的事情之一是duck typing,并且Numpy通常与该设计方法非常兼容。假设您具有仅矢量功能vecfunc。您可以在函数的开头添加一些样板,将任何一维数组膨胀为1 x n向量:

def vecfunc(arr):
    if arr.ndim==1:
        arr = arr[None, :]

    ...function body goes here...

这将避免由于arr尺寸过小而引起的任何问题,并且在大多数情况下仍可能给出正确的行为。但是,它并不能阻止用户传入r x n x m数组或15 x n数组。最终,您将不得不使用方法3.来处理大量此类问题,并在适当的地方抛出一些异常。例如:

def vecfunc(arr):
    if not 0 < arr.ndim < 3:
        raise ValueError("arr must have ndim of 1 or 2. arr.ndim: %d" % arr.ndim)
    elif arr.ndim==1:
        arr = arr[None, :]

如果让您感觉更好,numpyscipy的代码库在许多函数中,何时何地都需要进行基于形状的异常检查。

当然,在开发任何给定功能的最后阶段,您总是可以不添加此类异常检查。您可能会对产生合理行为的输入范围感到惊讶。

如果您对类型注释不满意,可以通过writing your code using Cython获得类似的信息。例如,如果您想要一个仅包含2D整数数组的add函数,则可以在.pyx文件中编写以下函数:

import numpy as np

def add(long[:, :] arr1, long[:, :] arr2):
    assert tuple(arr1.shape) == tuple(arr2.shape)

    result = np.zeros((arr1.shape[0], arr1.shape[1]), dtype=np.long)
    cdef long[:, :] result_view = result

    for x in range(arr1.shape[0]):
        for y in range(arr1.shape[1]):
            result_view[x, y] = arr1[x, y] + arr2[x, y]

    return result

有关编写和编译Cython的更多详细信息,请参见上面链接的文档。

这并不是真正的强类型输入,而是“类型注释”,但是它可以做您想要的。遗憾的是,我无法找到一种方法来固定单个尺寸的大小,而只能确定尺寸的总数。