NumPy 2.0 遷移指南#

本文檔包含一組關於如何更新您的程式碼以與 NumPy 2.0 協同運作的說明。它涵蓋了 NumPy 的 Python 和 C API 中的變更。

注意

請注意,NumPy 2.0 也破壞了二進制相容性 - 如果您正在為依賴 NumPy C API 的 Python 套件發佈二進制檔案,請參閱NumPy 2.0 特定建議

Ruff 外掛程式#

在 2.0 發行說明和本遷移指南中涵蓋的許多變更,可以使用專用的 Ruff 規則,即規則 NPY201,在下游程式碼中自動調整。

您應該安裝 ruff>=0.4.8 並將 NPY201 規則添加到您的 pyproject.toml

[tool.ruff.lint]
select = ["NPY201"]

您也可以直接從命令列應用 NumPy 2.0 規則

$ ruff check path/to/code/ --select NPY201

NumPy 資料類型提升的變更#

NumPy 2.0 根據 NEP 50 變更了提升(組合不同資料類型的結果)。請參閱 NEP 以獲取有關此變更的詳細資訊。它包含範例變更表和向後相容性章節。

最大的向後相容性變更是現在一致地保留了純量的精度。兩個例子是

  • np.float32(3) + 3. 現在返回 float32,而之前返回 float64。

  • np.array([3], dtype=np.float32) + np.float64(3) 現在將返回 float64 陣列。(純量的較高精度不會被忽略。)

對於浮點數值,這可能會導致在使用純量時產生較低的精度結果。對於整數,可能會發生錯誤或溢位。

為了解决這個問題,您可以顯式轉換。通常,確保您透過 int()float()numpy_scalar.item() 使用 Python 純量也可能是一個好的解決方案。

要追蹤變更,您可以啟用針對已變更行為發出警告(使用 warnings.simplefilter 將其作為回溯錯誤引發)

np._set_promotion_state("weak_and_warn")

這在測試期間很有用。不幸的是,執行此操作可能會標記許多在實踐中無關緊要的變更。

Windows 預設整數#

NumPy 使用的預設整數現在在所有 64 位元系統上都是 64 位元(在 32 位元系統上是 32 位元)。由於與 Python 2 相關的歷史原因,它以前等同於 C long 類型。預設整數現在等同於 np.intp

大多數終端使用者不應受到此變更的影響。某些操作將使用更多記憶體,但某些操作實際上可能會變得更快。如果您因呼叫以編譯語言編寫的函式庫而遇到問題,則顯式轉換為 long 可能會有幫助,例如使用:arr = arr.astype("long", copy=False)

如果使用 C 端的 long 或等效類型,則以 C、Cython 或類似語言編寫的與編譯程式碼介接的函式庫可能需要更新以適應使用者輸入。在這種情況下,您可能希望使用 intp 並轉換使用者輸入或同時支援 longintp(以更好地支援 NumPy 1.x)。在 C 或 Cython 中建立新的整數陣列時,新的 NPY_DEFAULT_INT 巨集將評估為 NPY_LONGNPY_INTP,具體取決於 NumPy 版本。

請注意,NumPy 隨機 API 不受此變更的影響。

C-API 變更#

由於過時或無法維護,某些定義已被移除或取代。某些新的 API 定義在 NumPy 2.0 和 NumPy 1.x 之間將在執行時以不同的方式評估。有些定義在 numpy/_core/include/numpy/npy_2_compat.h 中(例如 NPY_DEFAULT_INT),可以完整或部分地供應商化,以便在針對 NumPy 1.x 進行編譯時可以使用這些定義。

如有必要,可以使用 PyArray_RUNTIME_VERSION >= NPY_2_0_API_VERSION 在 NumPy 1.x 和 2.0 上明確實作不同的行為。(相容性標頭以與此類使用相容的方式定義它。)

如果您在此處需要其他解決方案,請告知我們。

PyArray_Descr 結構已變更#

最具影響力的 C-API 變更之一是 PyArray_Descr 結構現在更加不透明,以便我們添加其他標誌並使項目大小不受 int 大小的限制,以及允許在未來改進結構化 dtype 並且不會讓新的 dtype 背負它們的欄位。

僅使用類型編號和其他初始欄位的程式碼不受影響。大多數程式碼有望主要存取 ->elsize 欄位,當 dtype/描述符本身附加到陣列時(例如 arr->descr->elsize),最好用 PyArray_ITEMSIZE(arr) 取代。

在不可能的情況下,需要新的存取器函數

  • PyDataType_ELSIZEPyDataType_SET_ELSIZE(請注意,結果現在是 npy_intp 而不是 int)。

  • PyDataType_ALIGNMENT

  • PyDataType_FIELDSPyDataType_NAMESPyDataType_SUBARRAY

  • PyDataType_C_METADATA

Cython 程式碼應使用 Cython 3,在這種情況下,變更是透明的。(僅針對 NumPy 2 編譯時,結構存取可用於 elsize 和對齊。)

為了與 1.x 和 2.x 一起編譯,如果您使用這些新的存取器,則不幸的是必須透過類似以下的巨集在本機定義它們

#if NPY_ABI_VERSION < 0x02000000
  #define PyDataType_ELSIZE(descr) ((descr)->elsize)
#endif

或將 npy2_compat.h 新增到您的程式碼庫中,並在與 NumPy 1.x 一起編譯時顯式包含它(因為它們是新的 API)。包含該檔案對 NumPy 2 沒有任何影響。

如果您需要協助或提供的函數不足,請隨時開啟 NumPy 問題。

自訂使用者 DType: 現有的使用者 dtype 現在必須使用 PyArray_DescrProto 來定義它們的 dtype 並稍微修改程式碼。請參閱 PyArray_RegisterDataType 中的註釋。

已移動到需要 import_array() 的標頭的功能#

如果您之前僅包含 ndarraytypes.h,您可能會發現某些功能不再可用,並且需要包含 ndarrayobject.h 或類似檔案。當將 npy_2_compat.h 供應商化到您自己的程式碼庫中以允許在使用 NumPy 1.x 編譯時使用新定義時,也需要此包含。

以前不需要匯入包含的功能

  • 用於存取 dtype 標誌的函數:PyDataType_FLAGCHKPyDataType_REFCHK 以及相關的 NPY_BEGIN_THREADS_DESCR

  • PyArray_GETITEMPyArray_SETITEM

警告

重要的是,當使用 npy_2_compat.h 標頭時,必須使用 import_array() 機制來確保可以存取完整的 NumPy API。在大多數情況下,您的擴充模組可能已經呼叫了它。但是,如果沒有,我們添加了 PyArray_ImportNumPyAPI() 作為確保匯入 NumPy API 的首選方法。此函數在多次呼叫時是輕量級的,因此您可以將其插入到可能需要它的任何位置(如果您希望避免在模組匯入時進行設定)。

增加的最大維度數#

最大維度數(和引數)已增加到 64。這會影響 NPY_MAXDIMSNPY_MAXARGS 巨集。最好檢查它們的使用情況,我們通常建議您不要使用這些巨集(尤其是 NPY_MAXARGS),以便 NumPy 的未來版本可以移除對維度數量的此限制。

NPY_MAXDIMS 也用於在 C-API 中表示 axis=None,包括 PyArray_AxisConverter。後者將返回 -2147483648 作為軸(最小的整數值)。其他函數可能會因 AxisError: axis 64 is out of bounds for array of dimension 而出錯,在這種情況下,您需要傳遞 NPY_RAVEL_AXIS 而不是 NPY_MAXDIMSNPY_RAVEL_AXISnpy_2_compat.h 標頭中定義,並且與執行時相關(在 NumPy 1.x 上映射到 32,在 NumPy 2.x 上映射到 -2147483648)。

複數類型 - 底層類型變更#

所有複數類型的底層 C 類型都已變更為使用原生 C99 類型。雖然這些類型的記憶體佈局與 NumPy 1.x 中使用的類型保持一致,但 API 略有不同,因為不再可能直接欄位存取(例如 c.realc.imag)。

建議使用函數 npy_crealnpy_cimag(以及相應的 float 和 long double 變體)來檢索複數的實部或虛部,因為這些函數將同時適用於 NumPy 1.x 和 NumPy 2.x。已新增用於設定實部或虛部的新函數 npy_csetrealnpy_csetimag,以及相容性巨集 NPY_CSETREALNPY_CSETIMAG(以及相應的 float 和 long double 變體)。

底層類型在 C++ 下仍然是一個結構(以上所有內容仍然有效)。

這對 Cython 有影響。建議始終使用原生 typedef cfloat_tcdouble_tclongdouble_t 而不是 NumPy 類型 npy_cfloat 等,除非您必須與使用 NumPy 類型編寫的 C 程式碼介接。您仍然可以使用 c.realc.imag 屬性(使用原生 typedef)編寫 cython 程式碼,但您不能再在 Cython 的 c++ 模式中使用原地運算符 c.imag += 1

由於 NumPy 2 現在包含 complex.h,因此使用名為 I 的變數的程式碼可能會看到類似以下的錯誤

要使用名稱 I,現在需要 #undef I

注意

NumPy 2.0.1 簡要地包含了 #undef I,以幫助尚未包含 complex.h 的使用者。

命名空間的變更#

在 NumPy 2.0 中,某些函數、模組和常數已被移動或移除,以透過移除不必要或過時的功能並闡明 NumPy 的哪些部分被視為私有,使 NumPy 命名空間更加使用者友善。請參閱下表以獲取遷移指南。對於大多數變更,這意味著用向後相容的替代方案替換它。

有關更多詳細資訊,請參閱 NEP 52 — NumPy 2.0 的 Python API 清理

主命名空間#

np 命名空間的大約 100 個成員已被棄用、移除或移動到新位置。這樣做是為了減少混亂並建立僅一種存取給定屬性的方式。下表顯示了已移除的成員

已移除的成員

遷移指南

add_docstring

它仍然可以作為 np.lib.add_docstring 使用。

add_newdoc

它仍然可以作為 np.lib.add_newdoc 使用。

add_newdoc_ufunc

它是一個內部函數,沒有替代品。

alltrue

請改用 np.all

asfarray

請改用具有浮點 dtype 的 np.asarray

byte_bounds

現在可以在 np.lib.array_utils.byte_bounds 下使用

cast

請改用 np.asarray(arr, dtype=dtype)

cfloat

請改用 np.complex128

charrarray

它仍然可以作為 np.char.chararray 使用。

clongfloat

請改用 np.clongdouble

compare_chararrays

它仍然可以作為 np.char.compare_chararrays 使用。

compat

沒有替代品,因為不再支援 Python 2。

complex_

請改用 np.complex128

cumproduct

請改用 np.cumprod

DataSource

它仍然可以作為 np.lib.npyio.DataSource 使用。

deprecate

直接使用 warnings.warn 發出 DeprecationWarning,或使用 typing.deprecated

deprecate_with_doc

直接使用 warnings.warn 發出 DeprecationWarning,或使用 typing.deprecated

disp

請改用您自己的列印函數。

fastCopyAndTranspose

請改用 arr.T.copy()

find_common_type

請改用 numpy.promote_typesnumpy.result_type。為了實現 scalar_types 引數的語義,請使用 numpy.result_type 並傳遞 Python 值 00.00j

format_parser

它仍然可以作為 np.rec.format_parser 使用。

get_array_wrap

float_

請改用 np.float64

geterrobj

請改用 np.errstate 上下文管理器。

Inf

請改用 np.inf

Infinity

請改用 np.inf

infty

請改用 np.inf

issctype

請改用 issubclass(rep, np.generic)

issubclass_

請改用內建的 issubclass

issubsctype

請改用 np.issubdtype

mat

請改用 np.asmatrix

maximum_sctype

請改用特定的 dtype。您應該避免依賴任何隱式機制,並在程式碼中顯式選擇一種種類的最大 dtype。

NaN

請改用 np.nan

nbytes

請改用 np.dtype(<dtype>).itemsize

NINF

請改用 -np.inf

NZERO

請改用 -0.0

longcomplex

請改用 np.clongdouble

longfloat

請改用 np.longdouble

lookfor

直接搜尋 NumPy 的文檔。

obj2sctype

請改用 np.dtype(obj).type

PINF

請改用 np.inf

product

請改用 np.prod

PZERO

請改用 0.0

recfromcsv

請改用帶有逗號分隔符的 np.genfromtxt

recfromtxt

請改用 np.genfromtxt

round_

請改用 np.round

safe_eval

請改用 ast.literal_eval

sctype2char

請改用 np.dtype(obj).char

sctypes

請改為顯式存取 dtype。

seterrobj

請改用 np.errstate 上下文管理器。

set_numeric_ops

對於一般情況,請使用 PyUFunc_ReplaceLoopBySignature。對於 ndarray 子類別,請定義 __array_ufunc__ 方法並覆寫相關的 ufunc。

set_string_function

請改用 np.set_printoptions,並使用格式化器自訂列印 NumPy 物件。

singlecomplex

請改用 np.complex64

string_

請改用 np.bytes_

sometrue

請改用 np.any

source

請改用 inspect.getsource

tracemalloc_domain

現在可以從 np.lib 使用。

unicode_

請改用 np.str_

who

請改用 IDE 變數瀏覽器或 locals()

如果表格中沒有包含您正在使用但在 2.0 中移除的項目,則表示它是私有成員。您應該使用現有的 API,或者,如果不可行,請與我們聯繫並請求恢復已移除的項目。

下表列出了已棄用的成員,這些成員將在 2.0 之後的版本中移除

已棄用的成員

遷移指南

in1d

請改用 np.isin

row_stack

請改用 np.vstackrow_stackvstack 的別名)。

trapz

請改用 np.trapezoidscipy.integrate 函數。

最後,已移除一組內部列舉。由於它們未在下游函式庫中使用,因此我們不提供有關如何替換它們的任何資訊

[FLOATING_POINT_SUPPORTFPE_DIVIDEBYZEROFPE_INVALIDFPE_OVERFLOWFPE_UNDERFLOWUFUNC_BUFSIZE_DEFAULTUFUNC_PYVALS_NAMECLIPWRAPRAISEBUFSIZEALLOW_THREADSMAXDIMSMAY_SHARE_EXACTMAY_SHARE_BOUNDS]

numpy.lib 命名空間#

np.lib 中可用的大多數函數也存在於主命名空間中,這是它們的主要位置。為了明確如何存取每個公共函數,np.lib 現在是空的,僅包含少數專用的子模組、類別和函數

  • array_utilsformatintrospectmixinsnpyiostride_tricks 子模組,

  • ArrayteratorNumpyVersion 類別,

  • add_docstringadd_newdoc 函數,

  • tracemalloc_domain 常數。

如果您在從 np.lib 存取屬性時遇到 AttributeError,您應該嘗試從主 np 命名空間存取它。如果某個項目也從主命名空間中遺失,那麼您正在使用私有成員。您應該使用現有的 API,或者,如果不可行,請與我們聯繫並請求恢復已移除的項目。

numpy.core 命名空間#

np.core 命名空間現在正式成為私有,並已重新命名為 np._core。使用者永遠不應直接從 _core 中獲取成員 - 相反,應使用主命名空間來存取有問題的屬性。_core 模組的佈局將來可能會在沒有通知的情況下變更,這與遵守棄用期策略的公共模組相反。如果某個項目也從主命名空間中遺失,那麼您應該使用現有的 API,或者,如果不可行,請與我們聯繫並請求恢復已移除的項目。

ndarray 和純量方法#

已從 np.ndarraynp.generic 純量類別中移除了一些方法。下表提供了已移除成員的替代方案

已過期的成員

遷移指南

newbyteorder

請改用 arr.view(arr.dtype.newbyteorder(order))

ptp

請改用 np.ptp(arr, ...)

setitem

請改用 arr[index] = value

numpy.strings 命名空間#

已建立新的 numpy.strings 命名空間,其中大多數字串操作都實作為 ufunc。numpy.char 舊的命名空間仍然可用,並且在可能的情況下,使用新的 ufunc 以獲得更高的效能。我們建議今後使用 strings 函數。char 命名空間將來可能會被棄用。

其他變更#

關於 pickled 檔案的注意事項#

NumPy 2.0 旨在載入使用 NumPy 1.26 建立的 pickle 檔案,反之亦然。對於 1.25 及更早版本,載入 NumPy 2.0 pickle 檔案將會拋出例外。

適應 copy 關鍵字中的變更#

複製關鍵字行為變更asarrayarrayndarray.__array__ 中可能需要這些變更

  • 使用 np.array(..., copy=False) 的程式碼在大多數情況下可以改成 np.asarray(...)。舊的程式碼傾向於像這樣使用 np.array,因為它比預設的 np.asarray 必要時複製行為的開銷更小。現在情況已非如此,np.asarray 是建議使用的函式。

  • 對於明確需要傳遞 None/False 以表示「必要時複製」,且需與 NumPy 1.x 和 2.x 相容的程式碼,請參閱 scipy#20172 以查看如何操作的範例。

  • 對於非 NumPy 類陣列物件上的任何 __array__ 方法,必須將 dtype=Nonecopy=None 關鍵字添加到簽名中 - 這也適用於舊版本的 NumPy(儘管舊版本的 numpy 永遠不會傳遞 copy 關鍵字)。如果關鍵字已添加到 __array__ 簽名中,則對於

    • copy=True 和任何 dtype 值,始終傳回新的副本,

    • copy=None 在需要時建立副本(例如,由 dtype 引起),

    • copy=False 絕不能建立副本。如果需要副本才能傳回 numpy 陣列或滿足 dtype,則引發例外 (ValueError)。

撰寫依賴 numpy 版本的程式碼#

明確地根據 numpy 版本進行分支的程式碼應該相當少見 - 在大多數情況下,程式碼可以重寫為同時與 1.x 和 2.0 相容。但是,如果確實有必要,以下是建議使用的程式碼模式,使用 numpy.lib.NumpyVersion

# example with AxisError, which is no longer available in
# the main namespace in 2.0, and not available in the
# `exceptions` namespace in <1.25.0 (example uses <2.0.0b1
# for illustrative purposes):
if np.lib.NumpyVersion(np.__version__) >= '2.0.0b1':
    from numpy.exceptions import AxisError
else:
    from numpy import AxisError

此模式將正確運作,包括 NumPy 發行候選版本,這在 2.0.0 發行期間非常重要。