Rust 型別對應到 Python 型別
撰寫可由 Python 呼叫的函式(例如 #[pyfunction] 或 #[pymethods] 區塊)時,函式引數需要 FromPyObject 特徵,回傳值需要 IntoPyObject 特徵。
請參考下一節的表格,找出 PyO3 提供且實作這些特徵的 Rust 型別。
引數型別
接受函式引數時,可以使用 Rust 程式庫型別或 PyO3 的 Python 原生型別。(何時使用哪種型別,請見下一節。)
下表列出 Python 型別及其對應可接受的函式引數型別:
| Python | Rust | Rust(Python 原生) |
|---|---|---|
object | - | PyAny |
str | String, Cow<str>, &str, char, OsString, PathBuf, Path | PyString |
bytes | Vec<u8>, &[u8], Cow<[u8]> | PyBytes |
bool | bool | PyBool |
int | i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize, num_bigint::BigInt1, num_bigint::BigUint1 | PyInt |
float | f32, f64, ordered_float::NotNan2, ordered_float::OrderedFloat2 | PyFloat |
complex | num_complex::Complex3 | PyComplex |
fractions.Fraction | num_rational::Ratio4 | - |
list[T] | Vec<T> | PyList |
dict[K, V] | HashMap<K, V>, BTreeMap<K, V>, hashbrown::HashMap<K, V>5, indexmap::IndexMap<K, V>6 | PyDict |
tuple[T, U] | (T, U), Vec<T> | PyTuple |
set[T] | HashSet<T>, BTreeSet<T>, hashbrown::HashSet<T>5 | PySet |
frozenset[T] | HashSet<T>, BTreeSet<T>, hashbrown::HashSet<T>5 | PyFrozenSet |
bytearray | Vec<u8>, Cow<[u8]> | PyByteArray |
slice | - | PySlice |
type | - | PyType |
module | - | PyModule |
collections.abc.Buffer | - | PyBuffer<T> |
datetime.datetime | SystemTime, chrono::DateTime<Tz>7, chrono::NaiveDateTime7 | PyDateTime |
datetime.date | chrono::NaiveDate7 | PyDate |
datetime.time | chrono::NaiveTime7 | PyTime |
datetime.tzinfo | chrono::FixedOffset7, chrono::Utc7, chrono_tz::TimeZone8 | PyTzInfo |
datetime.timedelta | Duration, chrono::Duration7 | PyDelta |
decimal.Decimal | rust_decimal::Decimal9 | - |
decimal.Decimal | bigdecimal::BigDecimal10 | - |
ipaddress.IPv4Address | std::net::IpAddr, std::net::Ipv4Addr | - |
ipaddress.IPv6Address | std::net::IpAddr, std::net::Ipv6Addr | - |
os.PathLike | PathBuf, Path | PyString |
pathlib.Path | PathBuf, Path | PyString |
typing.Optional[T] | Option<T> | - |
typing.Sequence[T] | Vec<T> | PySequence |
typing.Mapping[K, V] | HashMap<K, V>, BTreeMap<K, V>, hashbrown::HashMap<K, V>5, indexmap::IndexMap<K, V>6 | &PyMapping |
typing.Iterator[Any] | - | PyIterator |
typing.Union[...] | See #[derive(FromPyObject)] | - |
另外也值得記住以下特殊型別:
| 項目 | 說明 |
|---|---|
Python<'py> | 用來證明已附加到 Python 直譯器的 token。 |
Bound<'py, T> | 具有生命週期的 Python 物件,將其綁定到 Python 直譯器的附加狀態。可存取大多數 PyO3 API。 |
Py<T> | 不連結任何直譯器附加生命週期的 Python 物件,可傳送至其他執行緒。 |
PyRef<T> | 不可變借用的 #[pyclass]。 |
PyRefMut<T> | 可變借用的 #[pyclass]。 |
更多關於將 #[pyclass] 作為函式引數的細節,請見本指南的Python 類別章節。
使用 Rust 程式庫型別 vs Python 原生型別
使用 Rust 函式庫型別作為函式引數,相較於 Python 原生型別會產生轉換成本。使用 Python 原生型別幾乎是零成本(只需進行類似 Python 內建函式 isinstance() 的型別檢查)。
不過,一旦付出轉換成本,Rust 標準函式庫型別會帶來多項好處:
- 你可以用原生速度的 Rust 程式碼實作功能(不受 Python 執行期成本影響)。
- 可與 Rust 生態系的其他元件有更好的互通性。
- 你可使用
Python::detach從直譯器分離,讓其他 Python 執行緒在 Rust 程式碼執行時繼續前進。 - 你也會受益於更嚴格的型別檢查。例如指定
Vec<i32>,只會接受包含整數的 Pythonlist。相對的 Python 原生等價型別&PyList會接受包含任何型別 Python 物件的list。
對於多數 PyO3 使用情境,付出轉換成本以換取上述好處是值得的。一如往常,若不確定是否值得,請進行基準測試!
將 Rust 值回傳給 Python
當從可由 Python 呼叫的函式回傳值時,可零成本使用 PyO3 的智慧指標(Py<T>、Bound<'py, T> 與 Borrowed<'a, 'py, T>)。
由於 Bound<'py, T> 與 Borrowed<'a, 'py, T> 具有生命週期參數,Rust 編譯器可能會要求你在函式中補上生命週期標註。請參考指南中專門章節。
若函式可能失敗,應回傳 PyResult<T> 或 Result<T, E>,其中 E 需實作 From<E> for PyErr。若回傳 Err 變體,將拋出 Python 例外。
最後,以下 Rust 型別也可作為回傳值轉換為 Python:
| Rust 型別 | 對應的 Python 型別 |
|---|---|
String | str |
&str | str |
bool | bool |
Any integer type (i32, u32, usize, etc) | int |
f32, f64 | float |
Option<T> | Optional[T] |
(T, U) | Tuple[T, U] |
Vec<T> | List[T] |
Cow<[u8]> | bytes |
HashMap<K, V> | Dict[K, V] |
BTreeMap<K, V> | Dict[K, V] |
HashSet<T> | Set[T] |
BTreeSet<T> | Set[T] |
Py<T> | T |
Bound<T> | T |
PyRef<T: PyClass> | T |
PyRefMut<T: PyClass> | T |