6.8. Операторы

Первый вид математики, которую numpy выполняет над ndarray, – это стандартные операторы Python. Арифметические операторы, операторы сравнения и побитовые операторы работают поэлементно – каждый оператор проходит массив (или оба массива) один раз от начала до конца внутри одного вызова библиотеки, что намного быстрее, чем эквивалентный цикл for на Python.

6.8.1. Арифметика

+, -, *, /, //, %, ** работают между двумя массивами совместимой формы или между массивом и скаляром:

a = np.array([1, 2, 3, 4], dtype=np.float)
b = np.array([10, 20, 30, 40], dtype=np.float)

print(a + b)        # array([11.0, 22.0, 33.0, 44.0])
print(a * 2)        # array([2.0, 4.0, 6.0, 8.0])
print(b - a)        # array([9.0, 18.0, 27.0, 36.0])
print(b / a)        # array([10.0, 10.0, 10.0, 10.0])

Dtype результата следует правилам повышения типов, описанным в Dtypes. Целочисленные массивы переполняются по модулю; приведите их к более широкому dtype перед операцией, когда это важно.

Оператор матричного умножения @ не реализован. Используйте dot() для матричных / векторных произведений.

6.8.1.1. Формы на месте

Каждый арифметический оператор имеет форму на месте – +=, -=, *=, /=, %=, **=. Форма на месте записывает результат прямо в существующий буфер, а не выделяет временный:

b = b + 1            # allocates a temporary the size of b
b += 1               # no temporary

На микроконтроллере вторая форма по сути обязательна для любого горячего цикла.

6.8.2. Побитовые операции

Побитовые операторы &, |, ^ работают поэлементно над целочисленными массивами. Применённые к массиву float или complex, они вызывают TypeError:

a = np.array([0b1100, 0b1010], dtype=np.uint8)
b = np.array([0b1010, 0b1100], dtype=np.uint8)
print(a & b)        # array([8, 8], dtype=uint8)
print(a | b)        # array([14, 14], dtype=uint8)
print(a ^ b)        # array([6, 6], dtype=uint8)

Унарный ~ выполняет побитовое НЕ над целочисленным массивом.

Операторы сдвига << и >> не подключены на уровне операторов Python. Функциональные формы left_shift() и right_shift() работают:

np.left_shift(a, 2)
np.right_shift(b, 1)

6.8.3. Сравнение

==, !=, <, <=, >, >= все возвращают ndarray типа bool формы широковещания:

a = np.array([1, 2, 3, 4, 5], dtype=np.uint8)
print(a < 3)
# array([True, True, False, False, False], dtype=bool)

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

6.8.3.1. Правило стороны

ndarray должен находиться слева от оператора отношения при сравнении со скаляром. a > 2 работает; 2 < a вызывает TypeError. Для симметричной формы используйте имена функций:

np.greater(5, a)        # 5 > a, element-wise
np.less(5, a)           # 5 < a, element-wise
np.equal(5, a)          # 5 == a, element-wise
np.not_equal(5, a)      # 5 != a, element-wise

6.8.4. Унарные операторы

  • +a – возвращает копию массива.

  • -a – отрицание. На беззнаковых dtype значения переполняются по модулю \(2^N\) так же, как и у бинарных операторов.

  • abs(a) – поэлементное абсолютное значение. На беззнаковых dtype возвращает копию без вычислений.

  • ~a – побитовая инверсия (только целочисленные массивы).

  • len(a) – возвращает длину первой оси, согласно соглашению о последовательностях Python.

6.8.5. Чего не хватает

Правосторонние операторы для сравнения и некоторых побитовых операций не реализованы так, как арифметические операторы. Используйте функциональные формы (выше), когда ndarray оказывается справа.

Полный список поддерживаемых операторов и правила повышения типов, которым они следуют, см. в numpy — numpy-совместимые операции с массивами.