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
並轉換使用者輸入或同時支援 long
和 intp
(以更好地支援 NumPy 1.x)。在 C 或 Cython 中建立新的整數陣列時,新的 NPY_DEFAULT_INT
巨集將評估為 NPY_LONG
或 NPY_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_ELSIZE
和PyDataType_SET_ELSIZE
(請注意,結果現在是npy_intp
而不是int
)。PyDataType_ALIGNMENT
PyDataType_FIELDS
、PyDataType_NAMES
、PyDataType_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_FLAGCHK
、PyDataType_REFCHK
以及相關的NPY_BEGIN_THREADS_DESCR
。PyArray_GETITEM
和PyArray_SETITEM
。
警告
重要的是,當使用 npy_2_compat.h
標頭時,必須使用 import_array()
機制來確保可以存取完整的 NumPy API。在大多數情況下,您的擴充模組可能已經呼叫了它。但是,如果沒有,我們添加了 PyArray_ImportNumPyAPI()
作為確保匯入 NumPy API 的首選方法。此函數在多次呼叫時是輕量級的,因此您可以將其插入到可能需要它的任何位置(如果您希望避免在模組匯入時進行設定)。
增加的最大維度數#
最大維度數(和引數)已增加到 64。這會影響 NPY_MAXDIMS
和 NPY_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_MAXDIMS
。NPY_RAVEL_AXIS
在 npy_2_compat.h
標頭中定義,並且與執行時相關(在 NumPy 1.x 上映射到 32,在 NumPy 2.x 上映射到 -2147483648
)。
複數類型 - 底層類型變更#
所有複數類型的底層 C 類型都已變更為使用原生 C99 類型。雖然這些類型的記憶體佈局與 NumPy 1.x 中使用的類型保持一致,但 API 略有不同,因為不再可能直接欄位存取(例如 c.real
或 c.imag
)。
建議使用函數 npy_creal
和 npy_cimag
(以及相應的 float 和 long double 變體)來檢索複數的實部或虛部,因為這些函數將同時適用於 NumPy 1.x 和 NumPy 2.x。已新增用於設定實部或虛部的新函數 npy_csetreal
和 npy_csetimag
,以及相容性巨集 NPY_CSETREAL
和 NPY_CSETIMAG
(以及相應的 float 和 long double 變體)。
底層類型在 C++ 下仍然是一個結構(以上所有內容仍然有效)。
這對 Cython 有影響。建議始終使用原生 typedef cfloat_t
、cdouble_t
、clongdouble_t
而不是 NumPy 類型 npy_cfloat
等,除非您必須與使用 NumPy 類型編寫的 C 程式碼介接。您仍然可以使用 c.real
和 c.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 |
它仍然可以作為 |
add_newdoc |
它仍然可以作為 |
add_newdoc_ufunc |
它是一個內部函數,沒有替代品。 |
alltrue |
請改用 |
asfarray |
請改用具有浮點 dtype 的 |
byte_bounds |
現在可以在 |
cast |
請改用 |
cfloat |
請改用 |
charrarray |
它仍然可以作為 |
clongfloat |
請改用 |
compare_chararrays |
它仍然可以作為 |
compat |
沒有替代品,因為不再支援 Python 2。 |
complex_ |
請改用 |
cumproduct |
請改用 |
DataSource |
它仍然可以作為 |
deprecate |
直接使用 |
deprecate_with_doc |
直接使用 |
disp |
請改用您自己的列印函數。 |
fastCopyAndTranspose |
請改用 |
find_common_type |
請改用 |
format_parser |
它仍然可以作為 |
get_array_wrap |
|
float_ |
請改用 |
geterrobj |
請改用 np.errstate 上下文管理器。 |
Inf |
請改用 |
Infinity |
請改用 |
infty |
請改用 |
issctype |
請改用 |
issubclass_ |
請改用內建的 |
issubsctype |
請改用 |
mat |
請改用 |
maximum_sctype |
請改用特定的 dtype。您應該避免依賴任何隱式機制,並在程式碼中顯式選擇一種種類的最大 dtype。 |
NaN |
請改用 |
nbytes |
請改用 |
NINF |
請改用 |
NZERO |
請改用 |
longcomplex |
請改用 |
longfloat |
請改用 |
lookfor |
直接搜尋 NumPy 的文檔。 |
obj2sctype |
請改用 |
PINF |
請改用 |
product |
請改用 |
PZERO |
請改用 |
recfromcsv |
請改用帶有逗號分隔符的 |
recfromtxt |
請改用 |
round_ |
請改用 |
safe_eval |
請改用 |
sctype2char |
請改用 |
sctypes |
請改為顯式存取 dtype。 |
seterrobj |
請改用 np.errstate 上下文管理器。 |
set_numeric_ops |
對於一般情況,請使用 |
set_string_function |
請改用 |
singlecomplex |
請改用 |
string_ |
請改用 |
sometrue |
請改用 |
source |
請改用 |
tracemalloc_domain |
現在可以從 |
unicode_ |
請改用 |
who |
請改用 IDE 變數瀏覽器或 |
如果表格中沒有包含您正在使用但在 2.0
中移除的項目,則表示它是私有成員。您應該使用現有的 API,或者,如果不可行,請與我們聯繫並請求恢復已移除的項目。
下表列出了已棄用的成員,這些成員將在 2.0
之後的版本中移除
已棄用的成員 |
遷移指南 |
---|---|
in1d |
請改用 |
row_stack |
請改用 |
trapz |
請改用 |
最後,已移除一組內部列舉。由於它們未在下游函式庫中使用,因此我們不提供有關如何替換它們的任何資訊
[FLOATING_POINT_SUPPORT
、FPE_DIVIDEBYZERO
、FPE_INVALID
、FPE_OVERFLOW
、FPE_UNDERFLOW
、UFUNC_BUFSIZE_DEFAULT
、UFUNC_PYVALS_NAME
、CLIP
、WRAP
、RAISE
、BUFSIZE
、ALLOW_THREADS
、MAXDIMS
、MAY_SHARE_EXACT
、MAY_SHARE_BOUNDS
]
numpy.lib 命名空間#
np.lib
中可用的大多數函數也存在於主命名空間中,這是它們的主要位置。為了明確如何存取每個公共函數,np.lib
現在是空的,僅包含少數專用的子模組、類別和函數
array_utils
、format
、introspect
、mixins
、npyio
和stride_tricks
子模組,Arrayterator
和NumpyVersion
類別,add_docstring
和add_newdoc
函數,tracemalloc_domain
常數。
如果您在從 np.lib
存取屬性時遇到 AttributeError
,您應該嘗試從主 np
命名空間存取它。如果某個項目也從主命名空間中遺失,那麼您正在使用私有成員。您應該使用現有的 API,或者,如果不可行,請與我們聯繫並請求恢復已移除的項目。
numpy.core 命名空間#
np.core
命名空間現在正式成為私有,並已重新命名為 np._core
。使用者永遠不應直接從 _core
中獲取成員 - 相反,應使用主命名空間來存取有問題的屬性。_core
模組的佈局將來可能會在沒有通知的情況下變更,這與遵守棄用期策略的公共模組相反。如果某個項目也從主命名空間中遺失,那麼您應該使用現有的 API,或者,如果不可行,請與我們聯繫並請求恢復已移除的項目。
ndarray 和純量方法#
已從 np.ndarray
和 np.generic
純量類別中移除了一些方法。下表提供了已移除成員的替代方案
已過期的成員 |
遷移指南 |
---|---|
newbyteorder |
請改用 |
ptp |
請改用 |
setitem |
請改用 |
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
關鍵字中的變更#
複製關鍵字行為變更 在 asarray
、array
和 ndarray.__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=None
和copy=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 發行期間非常重要。