Массивы numpy для проверки типов Python включают их dtype

Я могу проверить, что моя функция получает входные данные правильного типа, используя:

def foo(x: np.ndarray, y: float):
    return x * y

Убедитесь, что если я попытаюсь использовать эту функцию с x, который не является np.ndarray, я получу ошибку еще до запуска кода.

Чего я не знаю, так это как проверить тип массива. Например:

 def return_valid_points_only(points: np.ndarray, valid: np.ndarray):
    assert points.shape == valid.shape
    return points[valid]

Я хочу проверить, что valid является не только np.ndarray, но и valid.dtype == bool.

В этом примере, если valid будет снабжен 0 и 1, чтобы указать достоверность, программа не выйдет из строя, и я получу ужасные результаты.

Спасибо


person Shaq    schedule 28.01.2021    source источник
comment
вы пробовали valid.dtype == bool?   -  person juanpa.arrivillaga    schedule 28.01.2021
comment
Что вы используете для проверки типов? mypy? IDE?   -  person hpaulj    schedule 28.01.2021
comment
редактировать - извините, IDE делает это сама   -  person Shaq    schedule 28.01.2021
comment
Эти проверки типизации предназначены для того, чтобы другие программисты могли легко понять функцию + если он использует ее неправильно (отправляя аргументы функции из неправильного типа) Pychrm сообщает об этом на месте   -  person Shaq    schedule 28.01.2021
comment
Я могу добавить скомпилированную строку, чтобы подтвердить это, но я хочу, чтобы Pychrm кричал на программиста, если он неправильно использует мою функцию, даже если он находится в другом файле.   -  person Shaq    schedule 28.01.2021
comment
Я думаю, мы зависим от функций PyCharm, чтобы удовлетворить ваши требования, а не от Python.   -  person fountainhead    schedule 28.01.2021
comment
Может быть, но текущий: np.ndarray совместим. Я могу запустить этот код позже и без Pycharm, поэтому он встроен в Python 3.7, а не только в функцию PyCharm.   -  person Shaq    schedule 28.01.2021
comment
Если вы используете тестирование Pycharm, включите его в файл tags.   -  person hpaulj    schedule 28.01.2021
comment
Спасибо @hpaulj. Можете ли вы уточнить, пожалуйста? Как должна выглядеть строка вызова функции? Должен ли я импортировать любую библиотеку? Вы также можете написать ответ.   -  person Shaq    schedule 28.01.2021
comment
Хотя некоторые функции numpy проверяют такие вещи, как размеры и dtype, чаще всего они просто пытаются преобразовать/принудить входные данные. Например, это может быть x = np.asarray(x, dtype=float) или x = np.atleast_2d(x).   -  person hpaulj    schedule 28.01.2021
comment
Ваше требование состоит в том, чтобы PyCharm знал и был чувствителен к тому факту, что тело вашей функции ожидает массивы, dtype которых bool. Я думаю, что многого можно ожидать от IDE. Если это предупреждение IDE действительно важно для вас, вы можете попробовать создать подкласс numpy массива bool dtype и определить свою функцию как принимающую массив вашего подкласса в качестве второго аргумента, а не массив numpy. Но это может быть излишним, потому что создание подклассов массивов numpy немного сложнее, чем создание подклассов классов Python.   -  person fountainhead    schedule 28.01.2021
comment
Когда вы передаете обычный массив numpy в свою функцию, с точки зрения Python (и, следовательно, с точки зрения PyCharm), независимо от того, имеет ли передаваемый массив bool dtype или какой-либо другой dtype, он фиксируется в атрибутах. объекта, а не в типе объекта. Чтобы удовлетворить ваше требование, PyCharm должен будет проверить атрибуты объекта аргумента, чего, я думаю, слишком много, чтобы ожидать от IDE.   -  person fountainhead    schedule 28.01.2021
comment
Более того, помните, что атрибут dtype можно изменить во время выполнения приложения. Итак, вы можете создать массив, состоящий из True и False с bool dtype. Во время выполнения вы можете изменить dtype на int, что по существу обойдёт все предупреждения IDE. Согласен, это был бы экстремальный сценарий, преднамеренный злой умысел программиста, а не ошибка программиста.   -  person fountainhead    schedule 28.01.2021
comment
Спасибо за полный ответ @fountainhead. Я не буду создавать подкласс, так как моя функция получает вызовы из многих мест в очень большом коде, поэтому я считаю, что это создаст больше беспорядка, чем на самом деле поможет.   -  person Shaq    schedule 28.01.2021


Ответы (1)


Python — это просьба о прощении, а не о разрешении. Это означает, что даже в вашем первом определении def foo(x: np.ndarray, y: float): действительно полагается на то, что пользователь примет подсказку, если только вы не используете что-то вроде mypy.

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

Numpy делает большую часть проверки за вас. Например, вместо того, чтобы ожидать массив, идиоматично принуждать его:

x = np.asanyarray(x)

np.asanyarray обычно является псевдонимом для array(a, dtype, copy=False, order=order, subok=True). Вы можете сделать что-то подобное для y:

y = np.asanyarray(y).item()

Это позволит использовать любой массив, если он имеет один элемент, скалярный или нет. Другой способ - уважать способность numpy транслировать массивы вместе, поэтому, если пользователь передает y как список из x.shape[-1] элементов.

Для вашей второй функции у вас есть несколько вариантов. Один из вариантов — разрешить причудливую индексацию. Поэтому, если пользователь передает список индексов вместо логической маски, вы можете использовать оба. Если, с другой стороны, вы настаиваете на логической маске, вы можете либо проверить, либо принудить dtype.

Если вы проверите, имейте в виду, что операция индексации numpy вызовет для вас ошибку, если размеры массива не совпадают. Вам нужно только проверить сам тип:

points = np.asanyarray(points)
valid = np.asanyarray(valid)
if valid.dtype != bool:
    raise ValueError('valid argument must be a boolean mask')

Если вместо этого вы выберете принуждение, пользователю будет разрешено использовать нули и единицы, но действительные входные данные не будут копироваться без необходимости:

valid = np.asanyarray(valid, bool)
person Mad Physicist    schedule 28.01.2021