Вступ
NumPy 2.x — це, мабуть, найбільш значуще оновлення фундаментальної бібліотеки для наукових обчислень у Python за останні роки. Перший реліз NumPy 2.0 вийшов у червні 2024 року, завершивши майже п'ятирічний цикл розробки. І, чесно кажучи, зміни вийшли настільки масштабними, що ландшафт Python-екосистеми для data science вже ніколи не буде таким, як раніше. Від початкового NumPy 2.0 до найновішого NumPy 2.4 (грудень 2025) бібліотека пройшла серйозну еволюцію — десятки нових функцій, покращення продуктивності та купа виправлень.
Чому NumPy 2.x настільки важливий?
По-перше, це перший major реліз з порушенням зворотної сумісності за понад 15 років. По-друге, він закладає фундамент для майбутнього розвитку всієї наукової екосистеми Python — включаючи pandas 3.0, scikit-learn та інші критичні пакети. І, нарешті, NumPy 2.x приносить суттєві покращення продуктивності — від 6x прискорення скалярних обчислень до 15x прискорення операцій з рядками. Так, ви не помилилися — до п'ятнадцяти разів швидше.
У цій статті ми детально розглянемо ключові зміни в кожній версії від 2.0 до 2.4, покажемо практичні приклади міграції коду, проаналізуємо вплив на продуктивність і подивимося, як все це працює разом із pandas 3.0, Polars та scikit-learn 1.8. Отже, поїхали.
Ключові зміни в NumPy 2.0
NEP 50: нові правила просування типів
Одна з найважливіших змін у NumPy 2.0 — впровадження NEP 50 (NumPy Enhancement Proposal 50). Ця пропозиція радикально переробила правила просування типів при операціях між масивами та скалярами. У старому NumPy 1.x Python-скаляри могли змінювати тип масиву абсолютно непередбачуваним чином, що призводило до втрати точності та, відверто кажучи, дуже дивної поведінки.
# NumPy 1.x — проблемна поведінка
import numpy as np
arr = np.array([1, 2, 3], dtype=np.int8)
result = arr + 200 # Переповнення! Результат: [-56, -54, -52]
# NumPy 2.x — безпечна поведінка
arr = np.array([1, 2, 3], dtype=np.int8)
result = arr + 200 # Автоматичне просування до int64
# Результат: [201, 202, 203]
Нові правила працюють так: коли ви виконуєте операцію між масивом та Python-скаляром, NumPy 2.x розглядає скаляр як «слабкий тип» і автоматично просуває його до типу масиву, якщо це можливо без втрати даних. Якщо ж просування небезпечне (як у прикладі вище з переповненням), бібліотека використовує безпечніший тип. Логічно, правда?
# Приклад з float32
arr_float32 = np.array([1.5, 2.5, 3.5], dtype=np.float32)
# NumPy 1.x: результат був би float64
# NumPy 2.x: результат залишається float32
result = arr_float32 * 2.0 # dtype=float32
print(result.dtype) # float32
# Але якщо операція вимагає вищої точності:
arr_small = np.array([1, 2, 3], dtype=np.int8)
result = arr_small * 256 # Автоматично просувається до int16/int32
print(result.dtype) # int16 або int32 (залежно від платформи)
Злам ABI: що це означає для екосистеми
NumPy 2.0 зламав Application Binary Interface (ABI). Якщо ви не знайомі з цим терміном — це означає, що пакети, скомпільовані з NumPy 1.x, несумісні на бінарному рівні з NumPy 2.x. Чесно кажучи, це була найбільш болюча зміна для екосистеми, адже вимагала перекомпіляції всіх залежних пакетів: pandas, scikit-learn, scipy, matplotlib та багатьох інших.
Але команда NumPy вжила заходів для пом'якшення цього переходу. Починаючи з NumPy 1.26, була введена шар сумісності ABI, яка дозволяє пакетам, скомпільованим з NumPy 1.x, працювати з NumPy 2.x на рівні API (хоча й з деякими обмеженнями). Це дало екосистемі час на адаптацію — і більшість популярних пакетів вже мігрували.
Зміна цілочисельного типу за замовчуванням на Windows
На платформах Windows NumPy 2.0 змінив цілочисельний тип за замовчуванням з int32 на int64, приводячи поведінку у відповідність з Linux та macOS. Ця зміна може здатися незначною, але вона усуває величезну кількість проблем з переносимістю коду між платформами.
# Перевірка типу за замовчуванням
arr = np.array([1, 2, 3])
print(arr.dtype)
# NumPy 1.x на Windows: int32
# NumPy 2.x на Windows: int64
# NumPy 1.x/2.x на Linux/macOS: int64 (завжди)
# Це впливає на індексацію та операції:
index = np.array([0, 1, 2]) # Тепер int64 на всіх платформах
Новий StringDType та простір імен numpy.strings
А ось ця зміна — одна з моїх улюблених. NumPy 2.0 представив експериментальний StringDType — новий тип даних змінної довжини для роботи з рядками UTF-8, який значно ефективніший за старий object dtype. Разом із ним з'явився новий простір імен numpy.strings з продуктивними ufunc-функціями для обробки рядків.
# Використання StringDType
arr_strings = np.array(['hello', 'world', 'numpy'], dtype=np.dtypes.StringDType())
# Операції з рядками через numpy.strings
result_upper = np.strings.upper(arr_strings)
print(result_upper) # ['HELLO', 'WORLD', 'NUMPY']
# Пошук підрядка
contains = np.strings.find(arr_strings, 'o')
print(contains) # [4, 1, -1] (індекси першого входження або -1)
# Заміна підрядків
replaced = np.strings.replace(arr_strings, 'o', '0')
print(replaced) # ['hell0', 'w0rld', 'numpy']
Підтримка Array API стандарту
NumPy 2.0 також впроваджує підтримку Array API стандарту — ініціативи, яка спрямована на уніфікацію API між різними бібліотеками масивів (NumPy, CuPy, JAX, PyTorch). По суті, ви пишете код один раз і він працює з будь-яким бекендом.
# np.isdtype — перевірка типу даних
print(np.isdtype(np.float32, 'real floating')) # True
print(np.isdtype(np.int64, 'integral')) # True
# np.vecdot — векторний скалярний добуток
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
result = np.vecdot(a, b)
print(result) # [17, 53]
Нові функції лінійної алгебри
NumPy 2.0 додав кілька нових функцій для лінійної алгебри, які роблять код більш виразним та ефективним. Якщо ви часто працюєте з матричними обчисленнями — оціните.
# matrix_norm — норма матриці
matrix = np.array([[1, 2], [3, 4]])
frobenius_norm = np.linalg.matrix_norm(matrix, 'fro')
print(frobenius_norm) # 5.477225575051661
# vector_norm — норма вектора
vector = np.array([3, 4])
l2_norm = np.linalg.vector_norm(vector)
print(l2_norm) # 5.0
# svdvals — лише сингулярні значення (швидше за повний SVD)
singular_values = np.linalg.svdvals(matrix)
print(singular_values) # [5.4649857 0.36596619]
Покращення var/std та нові unique_* функції
Функції обчислення дисперсії та стандартного відхилення отримали новий параметр correction замість старого ddof, а також параметр mean для використання попередньо обчисленого середнього. Крім того, функціональність np.unique() була розділена на спеціалізовані функції — і це робить код набагато чистішим.
# Новий параметр correction (замість ddof)
data = np.array([1, 2, 3, 4, 5])
variance = np.var(data, correction=1)
print(variance) # 2.5
# Використання попередньо обчисленого середнього
mean_value = np.mean(data)
variance_fast = np.var(data, mean=mean_value)
# Нові unique_* функції
data = np.array([1, 2, 2, 3, 3, 3, 4])
# unique_counts — лише значення та їх кількість
values, counts = np.unique_counts(data)
print(values) # [1, 2, 3, 4]
print(counts) # [1, 2, 3, 1]
# unique_inverse — для реконструкції
values, inverse = np.unique_inverse(data)
reconstructed = values[inverse]
print(np.array_equal(data, reconstructed)) # True
# unique_values — лише унікальні значення (найшвидша)
values = np.unique_values(data)
Властивість .mT для транспонування матриць
Нова властивість .mT дозволяє транспонувати останні два виміри багатовимірного масиву. Це особливо зручно при роботі з батчами матриць — більше не потрібно вручну переставляти осі.
# Транспонування матриць у батчі
batch_matrices = np.random.rand(10, 3, 4) # 10 матриць 3x4
# Нова версія (простіше та зрозуміліше):
transposed_new = batch_matrices.mT
print(transposed_new.shape) # (10, 4, 3)
# Для 3D+ масивів .mT та .T відрізняються:
arr_3d = np.random.rand(2, 3, 4)
print(arr_3d.T.shape) # (4, 3, 2) — повне транспонування
print(arr_3d.mT.shape) # (2, 4, 3) — транспонування лише матриць
Видалені функції та їх заміни
NumPy 2.0 видалив чимало застарілих функцій та API. Ось таблиця найважливіших змін, яку варто тримати під рукою при міграції:
| Видалено в NumPy 2.0 | Заміна | Примітка |
|---|---|---|
np.bool, np.int, np.float |
np.bool_, np.int_, np.float64 |
Уникнення конфлікту з вбудованими типами Python |
np.product() |
np.prod() |
Стандартизація назви |
np.cumproduct() |
np.cumprod() |
Стандартизація назви |
np.sometrue(), np.alltrue() |
np.any(), np.all() |
Більш зрозумілі назви |
np.find_common_type() |
np.result_type() |
Надійніше просування типів |
np.in1d() |
np.isin() |
Зрозуміліша назва |
np.trapz() |
np.trapezoid() |
Повна назва замість абревіатури |
np.Inf, np.NaN |
np.inf, np.nan |
Дотримання стандартів PEP 8 |
Еволюція NumPy 2.1–2.3
NumPy 2.1: покращення продуктивності
NumPy 2.1 вийшов у серпні 2024 і був зосереджений на покращенні продуктивності та стабільності після великого релізу 2.0. Серед основних покращень — оптимізація операцій з пам'яттю, швидші обчислення для малих масивів та покращена підтримка SIMD інструкцій на різних платформах.
# Покращення продуктивності для малих масивів у 2.1
small_arrays = [np.random.rand(10) for _ in range(1000)]
# Операції з малими масивами стали швидшими на 20-30%
result = [arr.sum() for arr in small_arrays]
# Оптимізована робота з view та копіями
large_array = np.random.rand(1000000)
view = large_array[::2] # View операція оптимізована
copy = large_array[::2].copy() # Copy також швидша
NumPy 2.2: array representation та __array_wrap__
NumPy 2.2 (грудень 2024) покращив текстове представлення масивів та переробив механізм __array_wrap__. Якщо ви коли-небудь писали власні підкласи ndarray — ви знаєте, наскільки це важливо для правильної інтеграції.
# Покращене відображення великих масивів
large_array = np.random.rand(100, 100)
print(large_array)
# Тепер показує більш компактне та інформативне представлення
# Покращена підтримка __array_wrap__ для підкласів
class MyArray(np.ndarray):
def __new__(cls, input_array):
obj = np.asarray(input_array).view(cls)
return obj
def __array_wrap__(self, result, context=None):
return np.ndarray.__array_wrap__(self, result, context)
my_arr = MyArray([1, 2, 3])
result = np.sqrt(my_arr) # Зберігає тип MyArray
NumPy 2.3: free-threaded Python та анотації
NumPy 2.3 (червень 2025) — це реліз, який готує ґрунт для справжньої революції в Python. Він додав покращену підтримку free-threaded Python (PEP 703), покращив type annotations для кращої інтеграції з type checkers та очистив застарілий код.
# Підтримка free-threaded Python (експериментально)
# Дозволяє справжню паралелізацію без GIL
import numpy as np
from concurrent.futures import ThreadPoolExecutor
def compute_intensive(arr):
return np.linalg.svd(arr)
# З free-threaded Python це може працювати паралельно
matrices = [np.random.rand(100, 100) for _ in range(10)]
with ThreadPoolExecutor() as executor:
results = list(executor.map(compute_intensive, matrices))
NumPy 2.4: останні нововведення
Нове приведення 'same_value'
NumPy 2.4 (грудень 2025) представив новий режим приведення типів 'same_value', який гарантує, що значення залишиться незмінним при конвертації типів. Це корисна штука, коли вам потрібна впевненість, що конвертація не зіпсує дані.
# Режим приведення 'same_value'
arr = np.array([1.0, 2.0, 3.0])
# Безпечне приведення до int (значення зберігаються)
int_arr = arr.astype(np.int64, casting='same_value')
print(int_arr) # [1, 2, 3]
# Це викличе помилку, якщо значення зміниться:
arr_with_fraction = np.array([1.5, 2.7, 3.9])
try:
int_arr = arr_with_fraction.astype(np.int64, casting='same_value')
except TypeError as e:
print(f"Помилка: {e}") # Неможливо безпечно привести
Параметр ndmax для np.array()
Новий параметр ndmax дозволяє контролювати максимальну глибину рекурсії при створенні масиву з вкладених послідовностей. Здається простим, але на практиці це може врятувати від неочікуваних багів при роботі зі складними структурами даних.
# Контроль вимірності за допомогою ndmax
data = [[1, 2], [3, 4]]
# За замовчуванням: створює 2D масив
a = np.array(data, dtype=object)
print(a.shape) # (2, 2)
# З ndmax=1: створює 1D масив списків
b = np.array(data, dtype=object, ndmax=1)
print(b.shape) # (2,)
Покращення StringDType та MaskedArray
StringDType продовжує еволюціонувати. Тепер він повністю підтримує fill_value при роботі з masked arrays, включаючи збереження через зрізи та views. Це робить його ще зручнішим для реальних завдань обробки даних.
# Використання StringDType з MaskedArray
import numpy.ma as ma
data = np.array(['apple', 'banana', '', 'cherry'],
dtype=np.dtypes.StringDType())
masked_data = ma.masked_array(data, mask=[False, False, True, False])
# Встановлення fill_value для пропущених значень
masked_data.fill_value = 'MISSING'
print(masked_data.filled()) # ['apple', 'banana', 'MISSING', 'cherry']
6x прискорення скалярних ufunc та 15x для unique()
Ось де стає по-справжньому цікаво. NumPy 2.4 приніс драматичне прискорення для universal functions (ufunc) при роботі зі скалярами — до 6 разів швидше. Різниця швидкості між math.sin та np.sin для скалярів зменшилася з 19x до лише 3x. А np.unique() для рядків стала до 15 разів швидшою завдяки hash-базованому алгоритму видалення дублікатів.
# Бенчмарк np.unique() для рядків
import time
string_data = np.array(['apple', 'banana', 'apple', 'cherry', 'banana'] * 10000,
dtype=np.dtypes.StringDType())
start = time.perf_counter()
unique_strings = np.unique(string_data)
end = time.perf_counter()
print(f"Час виконання: {(end - start) * 1000:.2f} мс")
# NumPy 2.4 з StringDType: ~5 мс
# NumPy 1.x з object dtype: ~75 мс
# Прискорення: ~15x
Протокол __numpy_dtype__ та інші нововведення
NumPy 2.4 також представив протокол __numpy_dtype__, який дозволяє користувацьким об'єктам визначати конвертацію в NumPy dtype через np.dtype(obj). Крім того, np.size() тепер підтримує кортеж осей, np.ndindex() став у 5.2 рази швидшим (завдяки використанню itertools.product), а понад 300 функцій отримали підтримку inspect.signature().
# Протокол __numpy_dtype__
class MyCustomType:
def __numpy_dtype__(self):
return np.dtype('float32')
# Використання з np.dtype()
custom = MyCustomType()
dt = np.dtype(custom)
print(dt) # float32
# np.size() з кількома осями
arr = np.random.rand(4, 5, 6)
size_multiple = np.size(arr, axis=(0, 1)) # 4 * 5 = 20
print(size_multiple) # 20
Застарілі та видалені функції в 2.4
NumPy 2.4 продовжив очищення API — процес, який команда розпочала ще у версії 2.0. Серед deprecated: встановлення ndarray.strides напряму (використовуйте stride_tricks.as_strided()), функція np.fix() (замінена на np.trunc()), модифікація ndarray.shape на місці (використовуйте np.reshape()). Серед видалених: np.in1d(), np.trapz() і MachAr для обчислення машинних лімітів.
Практичний посібник з міграції
Покроковий план міграції з NumPy 1.x
Міграція на NumPy 2.x вимагає систематичного підходу. Я пройшов через це на кількох проєктах і можу сказати — якщо дотримуватися плану, процес проходить набагато менш болісно, ніж здається на перший погляд.
Крок 1: Оцінка готовності залежностей
# Перевірте версії ваших залежностей
import importlib.metadata
critical_packages = {
'pandas': '2.1.0',
'scikit-learn': '1.3.0',
'scipy': '1.11.0',
'matplotlib': '3.7.0',
}
for package, min_ver in critical_packages.items():
try:
version = importlib.metadata.version(package)
status = "OK" if version >= min_ver else f"ОНОВИТИ до {min_ver}+"
print(f"{package}: {version} — {status}")
except importlib.metadata.PackageNotFoundError:
print(f"{package}: не встановлено")
Крок 2: Автоматичний аудит коду з ruff
# Встановіть ruff та запустіть перевірку NPY201
# pip install ruff
# ruff check --select NPY201 .
# Автоматичне виправлення:
# ruff check --select NPY201 --fix .
# Це автоматично замінить:
# np.bool → np.bool_
# np.float → np.float64
# np.product() → np.prod()
# np.sometrue() → np.any()
# np.alltrue() → np.all()
Крок 3: Виправлення типових помилок
# Помилка 1: AttributeError з видаленими типами
# Стара версія (помилка):
# dtype = np.bool
# Виправлення:
dtype = np.bool_
# Помилка 2: Зміни в просуванні типів
arr_int8 = np.array([100, 120, 127], dtype=np.int8)
# NumPy 1.x: arr_int8 + 200 → переповнення
# NumPy 2.x: arr_int8 + 200 → автоматичне просування до int64
# Помилка 3: np.gradient тепер повертає tuple замість list
result = np.gradient(np.array([1.0, 4.0, 9.0, 16.0]))
print(type(result)) # numpy.ndarray (для 1D)
# Для N-D масивів: тепер tuple замість list
# Помилка 4: np.unique() з рядками може повертати несортовані дані
# У NumPy 2.4 hash-based алгоритм не гарантує сортування
Крок 4: Тестування та валідація
# Скрипт для комплексної перевірки сумісності
import numpy as np
import warnings
def validate_numpy_migration():
issues = []
# Перевірка версії
version = tuple(int(x) for x in np.__version__.split('.')[:2])
if version < (2, 0):
issues.append(f"NumPy {np.__version__} — потрібна версія 2.0+")
# Перевірка правил просування типів
arr = np.array([1], dtype=np.int8)
result = arr + np.float64(1.0)
if result.dtype != np.float64:
issues.append("Несподіване просування типів")
# Перевірка StringDType
try:
s = np.array(['test'], dtype=np.dtypes.StringDType())
issues_str = False
except Exception:
issues.append("StringDType не підтримується")
if issues:
print("Проблеми міграції:")
for issue in issues:
print(f" - {issue}")
else:
print("Міграція успішна! Усі перевірки пройдені.")
validate_numpy_migration()
Продуктивність та оптимізація
Порівняння продуктивності NumPy 1.x vs 2.x
Давайте подивимося на реальні цифри. Нижче — детальне порівняння продуктивності між версіями NumPy на типових операціях:
# Бенчмарк-сюїта для типових операцій
import timeit
import numpy as np
def benchmark(stmt, setup='', number=1000):
t = timeit.timeit(stmt, setup=setup, globals=globals(), number=number)
return t / number * 1000 # мілісекунди
# 1. Скалярні ufunc (найбільше прискорення в 2.4)
arr = np.random.rand(1_000_000)
t = benchmark('np.sin(arr) + 2.0')
print(f"Скалярні ufunc: {t:.3f} мс")
# NumPy 1.x: ~12 мс → NumPy 2.4: ~2 мс (6x)
# 2. np.unique() для рядків
strings = np.array(['item_' + str(i % 100) for i in range(100_000)],
dtype=np.dtypes.StringDType())
t = benchmark('np.unique(strings)', number=100)
print(f"unique() рядки: {t:.3f} мс")
# NumPy 1.x (object): ~80 мс → NumPy 2.4: ~5 мс (15x)
# 3. np.ndindex() — 5.2x прискорення
t = benchmark('list(np.ndindex(10, 10, 10))', number=100)
print(f"ndindex: {t:.3f} мс")
Поради для максимальної продуктивності
Ось кілька практичних порад, які допоможуть витиснути максимум з NumPy 2.x:
# Порада 1: Використовуйте StringDType замість object
# Повільно (object dtype):
slow = np.array(['apple'] * 10000, dtype=object)
# Швидко (StringDType):
fast = np.array(['apple'] * 10000, dtype=np.dtypes.StringDType())
# Порада 2: Використовуйте спеціалізовані unique_* функції
data = np.random.randint(0, 100, 100_000)
# Якщо потрібні лише значення: unique_values (найшвидша)
values = np.unique_values(data)
# Якщо потрібні значення та кількість: unique_counts
values, counts = np.unique_counts(data)
# Порада 3: svdvals замість повного SVD
matrix = np.random.rand(500, 500)
# Повільно: U, s, Vh = np.linalg.svd(matrix)
# Швидко:
s = np.linalg.svdvals(matrix)
# Порада 4: Параметр mean у np.var()
mean = np.mean(data.astype(float))
var = np.var(data.astype(float), mean=mean) # Не обчислює mean повторно
Сумісність з екосистемою
Інтеграція з pandas 3.0, scikit-learn та Polars
NumPy 2.x — це фундамент всієї екосистеми наукового Python. Давайте подивимося, як він працює разом із найпопулярнішими інструментами:
# NumPy 2.x + pandas 3.0
import pandas as pd
import numpy as np
# pandas 3.0 повністю сумісний з NumPy 2.x
df = pd.DataFrame({
'values': np.random.rand(1000),
'categories': np.array(['A', 'B', 'C'] * 333 + ['A'],
dtype=np.dtypes.StringDType())
})
# NumPy 2.x + scikit-learn
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
X = np.random.rand(100, 5)
y = np.random.rand(100)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # NumPy 2.x під капотом
model = LinearRegression()
model.fit(X_scaled, y)
print(model.predict(X_scaled).dtype) # float64
# NumPy 2.x + Polars
import polars as pl
numpy_data = np.random.rand(1000, 3)
df_polars = pl.DataFrame({
'col1': numpy_data[:, 0],
'col2': numpy_data[:, 1],
'col3': numpy_data[:, 2]
})
# Polars → NumPy (використовує NumPy 2.x API)
back_to_numpy = df_polars.to_numpy()
print(type(back_to_numpy)) # numpy.ndarray
Free-threaded Python: перспективи
NumPy 2.3+ впроваджує підтримку free-threaded Python (PEP 703), і це, на мою думку, одна з найперспективніших змін. Справжня паралелізація без Global Interpreter Lock може стати революційною зміною для обчислювально інтенсивних завдань:
# Free-threaded NumPy (Python 3.13t+)
from concurrent.futures import ThreadPoolExecutor
import numpy as np
def matrix_operation(matrix):
"""Обчислювально інтенсивна операція"""
return np.linalg.svd(matrix, full_matrices=False)
matrices = [np.random.rand(500, 500) for _ in range(8)]
# Без GIL — справжня паралелізація на 8 ядрах
with ThreadPoolExecutor(max_workers=8) as executor:
results = list(executor.map(matrix_operation, matrices))
# Перевірка статусу free-threading:
import sys
free_threaded = hasattr(sys.flags, 'gil') and sys.flags.gil == 0
print(f"Free-threaded: {free_threaded}")
Порівняння версій NumPy 2.x
Щоб було легше орієнтуватися, ось зведена таблиця ключових нововведень у кожній версії:
| Функція | NumPy 2.0 | NumPy 2.1 | NumPy 2.2 | NumPy 2.3 | NumPy 2.4 |
|---|---|---|---|---|---|
| NEP 50 (просування типів) | Впроваджено | — | — | — | — |
| StringDType | Експериментально | Покращення | Покращення | Покращення | MaskedArray fill_value |
| Array API стандарт | Впроваджено | Розширення | Розширення | Розширення | Розширення |
| Free-threaded Python | — | — | — | Початкова підтримка | Покращена підтримка |
| Прискорення скалярних ufunc | — | — | — | — | 6x швидше |
| unique() для рядків | — | — | — | — | 15x швидше |
| Приведення 'same_value' | — | — | — | — | Додано |
| ndmax параметр | — | — | — | — | Додано |
| __numpy_dtype__ протокол | — | — | — | — | Додано |
| Signature introspection | — | — | — | — | 300+ функцій |
Висновки та рекомендації
NumPy 2.x — це не просто чергове оновлення бібліотеки, а фундаментальна зміна в екосистемі наукового Python. Від NEP 50, що переробила правила просування типів, до 15-кратного прискорення операцій з рядками у версії 2.4 — кожен реліз приносить щось суттєве.
Мігруйте зараз, якщо:
- Ви починаєте новий проєкт і хочете використовувати сучасний API
- Ви інтенсивно працюєте з рядковими даними — StringDType дає величезний виграш продуктивності
- Вам потрібна максимальна швидкість обчислень — прискорення до 6x-15x
- Ви вже використовуєте pandas 3.0 або scikit-learn 1.5+ — вони оптимізовані для NumPy 2.x
Почекайте з міграцією, якщо:
- У вас великий legacy codebase з критичними залежностями, які ще не підтримують NumPy 2.x
- Ваші процеси залежать від специфічної поведінки NumPy 1.x (наприклад, старі правила просування типів)
- У вас немає ресурсів на тестування та виправлення проблем сумісності
Використовуйте ruff --select NPY201 --fix для автоматичного виправлення більшості проблем сумісності. Починайте з тестового середовища, поступово оновлюйте залежності та ретельно тестуйте критичні компоненти. NumPy 2.x закладає фундамент для майбутнього Python data science — від free-threaded обчислень до GPU-прискорення через Array API. Час мігрувати — і ця стаття, сподіваюся, зробить цей процес трохи менш страшним.