Как Python 3 обеспечивает правильное измерение аргументов массива? - PullRequest
0 голосов
/ 12 ноября 2018

В моем новом проекте 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. Определить классы данных: OneRow, TwoRows, ThreeRows и FourPlusRows. У каждого есть поле ndarray, проверенное в конструкторе. Преимущество включает подсказки типа и лучшее моделирование предметной области, как DDD. Недостатком является дополнительная сложность.

Вопрос: Учитывая подсказки типов, введенные в Python 3, и тенденцию к функциональному программированию, каков текущий подход к решению этой проблемы?

1 Ответ

0 голосов
/ 13 ноября 2018

Одна из лучших вещей в Python - утка, набирающая , и 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, :]

Если вам от этого станет легче, базы кодов numpy и scipy имеют такие формы:основанные проверки исключений в ряде функций, когда и где они необходимы.

Конечно, вы всегда можете прекратить добавлять такие виды проверок исключений до самого конца разработки любой данной функции.Вы можете быть удивлены диапазоном ввода, обеспечивающим разумное поведение.

Если вы не можете использовать аннотации типов, вы можете получить нечто подобное, написав свой код с использованием Cython .Например, если вам нужна функция 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 см.Документы, ссылки на которые приведены выше.

Это не столько "аннотации типов", сколько настоящие строгие наборы, но может делать то, что вы хотите.К сожалению, я не смог найти способ зафиксировать размер одного измерения, только общее количество измерений.

...