精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

厭倦了C++,CS&ML博士用Rust重寫(xiě)Python擴(kuò)展,還總結(jié)了九條規(guī)則

開(kāi)發(fā) 開(kāi)發(fā)工具 后端
擁有 CS 與機(jī)器學(xué)習(xí)博士學(xué)位的 Carl M. Kadie,通過(guò)更新 Python 中生物信息學(xué)軟件包 Bed-Reader,為研究者帶來(lái)了在 Rust 中編寫(xiě) Python 擴(kuò)展的九個(gè)規(guī)則。

Python 是數(shù)據(jù)科學(xué)家最流行的編程語(yǔ)言之一,其內(nèi)部集成了高質(zhì)量分析庫(kù),包括 NumPy、SciPy、自然語(yǔ)言工具包等,這些庫(kù)中的許多都是用 C 和 C++ 實(shí)現(xiàn)的。

然而,C 和 C++ 兼容性差,且本身不提供線程安全。有研究者開(kāi)始轉(zhuǎn)向 Rust,重寫(xiě) C++ 擴(kuò)展。

擁有 CS 與機(jī)器學(xué)習(xí)博士學(xué)位的 Carl M. Kadie,通過(guò)更新 Python 中生物信息學(xué)軟件包 Bed-Reader,為研究者帶來(lái)了在 Rust 中編寫(xiě) Python 擴(kuò)展的九個(gè)規(guī)則。以下是原博客的主要內(nèi)容。

一年前,我厭倦了我們軟件包 Bed-Reader 的 C++ 擴(kuò)展,我用 Rust 重寫(xiě)了它,令人高興的是,得到的新擴(kuò)展和 C/C++ 一樣快,但具有更好的兼容性和安全性。一路走來(lái),我學(xué)會(huì)了這九條規(guī)則,可以幫助你創(chuàng)建更好的擴(kuò)展代碼,這九條規(guī)則包括:

  • 創(chuàng)建一個(gè)包含 Rust 和 Python 項(xiàng)目的單獨(dú)存儲(chǔ)庫(kù)
  • 使用 maturin & PyO3 在 Rust 中創(chuàng)建 Python-callable translator 函數(shù)
  • 讓 Rust translator 函數(shù)調(diào)用 nice Rust 函數(shù)
  • 在 Python 中預(yù)分配內(nèi)存
  • 將 nice Rust 錯(cuò)誤處理翻譯 nice Python 錯(cuò)誤處理
  • 多線程與 Rayon 和 ndarray::parallel,返回任何錯(cuò)誤
  • 允許用戶控制并行線程數(shù)
  • 將 nice 動(dòng)態(tài)類(lèi)型 Python 函數(shù)翻譯成 nice Rust 泛型函數(shù)
  • 創(chuàng)建 Rust 和 Python 測(cè)試

其中,文中提到的 nice 這個(gè)詞是指使用最佳實(shí)踐和原生類(lèi)型創(chuàng)建。換句話說(shuō):在代碼頂部,編寫(xiě) nice Python 代碼;在中間,用 Rust 編寫(xiě) translator 代碼;在底部,編寫(xiě) nice Rust 代碼。結(jié)構(gòu)如下圖所示:

上述策略看似顯而易見(jiàn),但遵循它可能會(huì)很棘手。本文提供了有關(guān)如何遵循每條規(guī)則的實(shí)用建議和示例。

我在 Bed-Reader 進(jìn)行了實(shí)驗(yàn),Bed-Reader 是一個(gè) Python 包,用于讀取和寫(xiě)入 PLINK Bed Files,這是一種在生物信息學(xué)中用于存儲(chǔ) DNA 數(shù)據(jù)的二進(jìn)制格式。Bed 格式的文件可以達(dá)到 TB。Bed-Reader 讓用戶可以快速、隨機(jī)地訪問(wèn)數(shù)據(jù)的子集。它在用戶選擇的 int8、float32 或 float64 中返回一個(gè) NumPy 數(shù)組。

我希望 Bed-Reader 擴(kuò)展代碼具有以下特點(diǎn):

  • 比 Python 快;
  • 兼容 NumPy;
  • 可以進(jìn)行數(shù)據(jù)并行多線程處理;
  • 與執(zhí)行數(shù)據(jù)并行多線程的所有其他包兼容;
  • 安全。

我們最初的 C++ 擴(kuò)展兼具速度快、與 NumPy 兼容,以及使用 OpenMP 進(jìn)行數(shù)據(jù)并行多線程等特點(diǎn)。遺憾的是,OpenMP 運(yùn)行時(shí)庫(kù) (Runtime library),存在 Python 包兼容版本問(wèn)題。

Rust 提供了 C++ 擴(kuò)展帶來(lái)的優(yōu)勢(shì)。除此之外,Rust 通過(guò)提供沒(méi)有運(yùn)行時(shí)庫(kù)的數(shù)據(jù)并行多線程解決了運(yùn)行時(shí)兼容性問(wèn)題。此外,Rust 編譯器還能保證線程安全。

在 Rust 中創(chuàng)建 Python 擴(kuò)展需要許多設(shè)計(jì)決策。根據(jù)我使用 Bed-Reader 的經(jīng)驗(yàn),以下是我的使用規(guī)則。

規(guī)則 1:創(chuàng)建一個(gè)包含 Rust 和 Python 項(xiàng)目的單獨(dú)存儲(chǔ)庫(kù)

下表顯示了如何布局文件:

使用 Rust 常用的‘cargo new’命令創(chuàng)建 Cargo.toml 和 src/lib.rs 文件。Python 沒(méi)有 setup.py 文件。相反,Cargo.toml 包含 PyPi 包信息,例如包的名稱(chēng)、版本號(hào)、 README 文件的位置等。要在沒(méi)有 setup.py 的情況下工作,pyproject.toml 必須包含:

  1. [build-system] 
  2. requires = ["maturin==0.12.5"] 
  3. build-backend = "maturin" 

一般來(lái)說(shuō),Python 設(shè)置在 pyproject.toml 中(如果不是,則在 pytest.ini 等文件中)。Python 代碼位于子文件夾 bed_reader 中。

最后,我們使用 GitHub 操作來(lái)構(gòu)建、測(cè)試和準(zhǔn)備部署。該腳本位于 .github/workflows/ci.yml 中。

規(guī)則 2:使用 maturin & PyO3在 Rust 中創(chuàng)建 Python-callable translator 函數(shù)

Maturin 是一個(gè) PyPi 包,可通過(guò) PyO3 構(gòu)建和發(fā)布 Python 擴(kuò)展。PyO3 是一個(gè) Rust crate,用于在 Rust 中編寫(xiě) Python 擴(kuò)展。

在 Cargo.toml 中,包含這些 Rust 依賴項(xiàng):

  1. [dependencies] 
  2. thiserror = "1.0.30" 
  3. ndarray-npy = { version = "0.8.1"default-features = false } 
  4. rayon = "1.5.1" 
  5. numpy = "0.15.0" 
  6. ndarray = { version = "0.15.4"features = ["approx", "rayon"] } 
  7. pyo3 = { version = "0.15.1"features = ["extension-module"] } 
  8. [dev-dependencies] 
  9. temp_testdir = "0.2.3" 

在 src/lib.rs 底部,包含這兩行:

  1. mod python_module; 
  2. mod tests; 

規(guī)則 3:Rust translator 函數(shù)調(diào)用 nice Rust 函數(shù)

在 src/lib.rs 定義了 nice Rust 函數(shù),這些函數(shù)將完成包的核心工作。它們能夠輸入和輸出標(biāo)準(zhǔn) Rust 類(lèi)型并嘗試遵循 Rust 最佳實(shí)踐。例如,對(duì)于 Bed-Reader 包, read_no_alloc 是一個(gè) nice Rust 函數(shù),用于從 PLINK Bed 文件讀取和返回值。

然而,Python 不能直接調(diào)用這些函數(shù)。因此,在文件 src/python_module.rs 中定義 Python 可以調(diào)用的 Rust translator 函數(shù),下面為 translator 函數(shù)示例:

  1. #[pyfn(m)] 
  2. #[pyo3(name = "read_f64")] 
  3. fn read_f64_py( 
  4.     _py: Python<'_>
  5.     filename: &str, 
  6.     iid_count: usize, 
  7.     sid_count: usize, 
  8.     count_a1: bool, 
  9.     iid_index: &PyArray1<usize>
  10.     sid_index: &PyArray1<usize>
  11.     val: &PyArray2<f64>
  12.     num_threads: usize, 
  13. ) -> Result<(), PyErr> { 
  14.     let iid_indexiid_index = iid_index.readonly(); 
  15.     let sid_indexsid_index = sid_index.readonly(); 
  16.     let mut val = unsafe { val.as_array_mut() }; 
  17.     let ii = &iid_index.as_slice()?; 
  18.     let si = &sid_index.as_slice()?; 
  19.     create_pool(num_threads)?.install(|| { 
  20.         read_no_alloc( 
  21.             filename, 
  22.             iid_count, 
  23.             sid_count, 
  24.             count_a1, 
  25.             ii, 
  26.             si, 
  27.             f64::NAN, 
  28.             &mut val, 
  29.        ) 
  30.     })?; 
  31.    Ok(()) 

該函數(shù)將文件名、一些與文件大小相關(guān)的整數(shù)以及兩個(gè)一維 NumPy 數(shù)組作為輸入,這些數(shù)組指示要讀取數(shù)據(jù)的哪個(gè)子集。該函數(shù)從文件中讀取值并填充 val,這是一個(gè)預(yù)先分配的二維 NumPy 數(shù)組。

注意

將 Python NumPy 1-D 數(shù)組轉(zhuǎn)換為 Rust slices,通過(guò):

  1. let iid_indexiid_index = iid_index.readonly(); 
  2. let ii = &iid_index.as_slice()?; 

將 Python NumPy 2d 數(shù)組轉(zhuǎn)換為 2-D Rust ndarray 對(duì)象,通過(guò):

  1. let mut val = unsafe { val.as_array_mut() }; 

調(diào)用 read_no_alloc,這是 src/lib.rs 中一個(gè) nice Rust 函數(shù),它將完成核心工作。

規(guī)則 4:在 Python 中預(yù)分配內(nèi)存

在 Python 中為結(jié)果預(yù)分配內(nèi)存簡(jiǎn)化了 Rust 代碼。在 Python 端,在 bed_reader/_open_bed.py 中,我們可以導(dǎo)入 Rust translator 函數(shù):

  1. from .bed_reader import [...] read_f64 [...] 

然后定義一個(gè) nice Python 函數(shù)來(lái)分配內(nèi)存、調(diào)用 Rust translator 函數(shù)并返回結(jié)果。

  1. def read([...]): 
  2.     [...] 
  3.     val = np.zeros((len(iid_index), len(sid_index)), orderorder=order, dtypedtype=dtype) 
  4.     [...] 
  5.     reader = read_f64 
  6.     [...] 
  7.     reader( 
  8.         str(self.filepath), 
  9.         iid_count=self.iid_count, 
  10.         sid_count=self.sid_count, 
  11.         count_a1=self.count_A1, 
  12.         iid_indexiid_index=iid_index, 
  13.         sid_indexsid_index=sid_index, 
  14.         valval=val, 
  15.         num_threadsnum_threads=num_threads, 
  16.     ) 
  17.     [...] 
  18.     return val 

規(guī)則 5:將 nice Rust 錯(cuò)誤處理翻譯 nice Python 錯(cuò)誤處理

為了了解如何處理錯(cuò)誤,讓我們?cè)?read_no_alloc( src/lib.rs 中 nice Rust 函數(shù))中跟蹤兩個(gè)可能的錯(cuò)誤。

示例錯(cuò)誤 1:來(lái)自標(biāo)準(zhǔn)函數(shù)的錯(cuò)誤。如果 Rust 的標(biāo)準(zhǔn) File::open 函數(shù)找不到文件或無(wú)法打開(kāi)文件, 在這種情況下,如下一行中的? 將導(dǎo)致函數(shù)返回一些 std::io::Error 值。

  1. let mut buf_reader = BufReader::new(File::open(filename)?); 

為了定義一個(gè)可返回這些值的函數(shù),我們可以給函數(shù)一個(gè)返回類(lèi)型 Result<(), BedErrorPlus>。我們定義 BedErrorPlus 時(shí)要包含所有 std::io::Error,如下所示:

  1. use thiserror::Error; 
  2. ... 
  3. /// BedErrorPlus enumerates all possible errors 
  4. /// returned by this library. 
  5. /// Based on https://nick.groenen.me/posts/rust-error-handling/#the-library-error-type 
  6. #[derive(Error, Debug)] 
  7. pub enum BedErrorPlus { 
  8.     #[error(transparent)] 
  9.     IOError(#[from] std::io::Error), 
  10.     #[error(transparent)] 
  11.     BedError(#[from] BedError), 
  12.     #[error(transparent)] 
  13.     ThreadPoolError(#[from] ThreadPoolBuildError), 

這是 nice Rust 錯(cuò)誤處理,但 Python 不理解它。因此,在 src/python_module.rs 中,我們要進(jìn)行翻譯。首先,定義 translator 函數(shù) read_f64_py 來(lái)返回 PyErr;其次,實(shí)現(xiàn)了一個(gè)從 BedErrorPlus 到 PyErr 的轉(zhuǎn)換器。轉(zhuǎn)換器使用正確的錯(cuò)誤消息創(chuàng)建正確的 Python 錯(cuò)誤類(lèi)(IOError、ValueError 或 IndexError)。如下所示:

  1. impl std::convert::From<BedErrorPlus> for PyErr { 
  2.    fn from(err: BedErrorPlus) -> PyErr { 
  3.         match err { 
  4.             BedErrorPlus::IOError(_) => PyIOError::new_err(err.to_string()), 
  5.             BedErrorPlus::ThreadPoolError(_) => PyValueError::new_err(err.to_string()), 
  6.             BedErrorPlus::BedError(BedError::IidIndexTooBig(_)) 
  7.             | BedErrorPlus::BedError(BedError::SidIndexTooBig(_)) 
  8.             | BedErrorPlus::BedError(BedError::IndexMismatch(_, _, _, _)) 
  9.             | BedErrorPlus::BedError(BedError::IndexesTooBigForFiles(_, _)) 
  10.             | BedErrorPlus::BedError(BedError::SubsetMismatch(_, _, _, _)) => { 
  11.                 PyIndexError::new_err(err.to_string()) 
  12.             } 
  13.             _ => PyValueError::new_err(err.to_string()), 
  14.         } 
  15.     } 

示例錯(cuò)誤 2:特定于函數(shù)的錯(cuò)誤。如果 nice 函數(shù) read_no_alloc 可以打開(kāi)文件,但隨后意識(shí)到文件格式錯(cuò)誤怎么辦?它應(yīng)該引發(fā)一個(gè)自定義錯(cuò)誤,如下所示:

  1. if (BED_FILE_MAGIC1 != bytes_vector[0]) || (BED_FILE_MAGIC2 != bytes_vector[1]) { 
  2.     return Err(BedError::IllFormed(filename.to_string()).into()); 

BedError::IllFormed 類(lèi)型的自定義錯(cuò)誤在 src/lib.rs 中定義:

  1. use thiserror::Error; 
  2. [...] 
  3. // https://docs.rs/thiserror/1.0.23/thiserror/ 
  4. #[derive(Error, Debug, Clone)] 
  5. pub enum BedError { 
  6.    #[error("Ill-formed BED file. BED file header is incorrect or length is wrong.'{0}'")] 
  7.    IllFormed(String), 
  8. [...] 

其余的錯(cuò)誤處理與示例錯(cuò)誤 1 中的相同。

最后,對(duì)于 Rust 和 Python,標(biāo)準(zhǔn)錯(cuò)誤和自定義錯(cuò)誤的結(jié)果都屬于帶有信息性錯(cuò)誤消息的特定錯(cuò)誤類(lèi)型。

規(guī)則 6:多線程與 Rayon 和 ndarray::parallel,返回任何錯(cuò)誤

Rust Rayon crate 提供了簡(jiǎn)單且輕量級(jí)的數(shù)據(jù)并行多線程。ndarray::parallel 模塊將 Rayon 應(yīng)用于數(shù)組。通常的模式是跨一個(gè)或多個(gè) 2D 數(shù)組的列(或行)并行化。面對(duì)的一個(gè)挑戰(zhàn)是從并行線程返回任何錯(cuò)誤消息。我將重點(diǎn)介紹兩種通過(guò)錯(cuò)誤處理并行化數(shù)組操作的方法。以下兩個(gè)示例都出現(xiàn)在 Bed-Reader 的 src/lib.rs 文件中。

方法 1:par_bridge().try_for_each

Rayon 的 par_bridge 將順序迭代器變成了并行迭代器。如果遇到錯(cuò)誤,使用 try_for_each 方法可以盡快停止所有處理。

這個(gè)例子中,我們遍歷了壓縮(zip)在一起的兩個(gè) things:

  • DNA 位置的二進(jìn)制數(shù)據(jù);
  • 輸出數(shù)組的列。

然后,按順序讀取二進(jìn)制數(shù)據(jù),但并行處理每一列的數(shù)據(jù)。我們停止了任何錯(cuò)誤。

  1. [... not shown, read bytes for DNA location's data ...] 
  2. // Zip in the column of the output array 
  3. .zip(out_val.axis_iter_mut(nd::Axis(1))) 
  4. // In parallel, decompress the iid info and put it in its column 
  5. .par_bridge() // This seems faster that parallel zip 
  6. .try_for_each(|(bytes_vector_result, mut col)| { 
  7.     match bytes_vector_result { 
  8.         Err(e) => Err(e), 
  9.         Ok(bytes_vector) => { 
  10.            for out_iid_i in 0..out_iid_count { 
  11.               let in_iid_i = iid_index[out_iid_i]; 
  12.               let i_div_4 = in_iid_i / 4; 
  13.               let i_mod_4 = in_iid_i % 4; 
  14.               let genotype_byte: u8 = (bytes_vector[i_div_4] >> (i_mod_4 * 2)) & 0x03; 
  15.               col[out_iid_i] = from_two_bits_to_value[genotype_byte as usize]; 
  16.             } 
  17.             Ok(()) 
  18.          } 
  19.       } 
  20. })?; 

方法 2:par_azip!

ndarray 包的 par_azip!宏允許并行地通過(guò)一個(gè)或多個(gè)壓縮在一起的數(shù)組或數(shù)組片段。在我看來(lái),這非常具有可讀性。但是,它不直接支持錯(cuò)誤處理。因此,我們可以通過(guò)將任何錯(cuò)誤保存到結(jié)果列表來(lái)添加錯(cuò)誤處理。

下面是一個(gè)效用函數(shù)的例子。完整的效用函數(shù)從三個(gè)計(jì)數(shù)和總和(count and sum)數(shù)組計(jì)算統(tǒng)計(jì)量(均值和方差),并且并行工作。如果在數(shù)據(jù)中發(fā)現(xiàn)錯(cuò)誤,則將該錯(cuò)誤記錄在結(jié)果列表中。在完成所有處理之后,檢查結(jié)果列表是否有錯(cuò)誤。

  1. [...] 
  2. let mut result_list: Vec<Result<(), BedError>> = vec![Ok(()); sid_count]; 
  3. nd::par_azip!((mut stats_row in stats.axis_iter_mut(nd::Axis(0)), 
  4.      &n_observed in &n_observed_array, 
  5.      &sum_s in &sum_s_array, 
  6.      &sum2_s in &sum2_s_array, 
  7.      result_ptr in &mut result_list) 
  8.   [...some code not shown...] 
  9. }); 
  10. // Check the result list for errors 
  11. result_list.par_iter().try_for_each(|x| (*x).clone())?; 
  12. [...] 

Rayon 和 ndarray::parallel 提供了許多其他不錯(cuò)的數(shù)據(jù)并行處理方法。

規(guī)則 7:允許用戶控制并行線程數(shù)

為了更好地使用用戶的其他代碼,用戶必須能夠控制每個(gè)函數(shù)可以使用的并行線程數(shù)。

在下面這個(gè) nice Python read 函數(shù)中,用戶可以得到一個(gè)可選的 num_threadsargument。如果用戶沒(méi)有設(shè)置它,Python 會(huì)通過(guò)這個(gè)函數(shù)設(shè)置它:

  1. def get_num_threads(num_threads=None): 
  2.     if num_threads is not None: 
  3.         return num_threads 
  4.     if "PST_NUM_THREADS" in os.environ: 
  5.         return int(os.environ["PST_NUM_THREADS"]) 
  6.     if "NUM_THREADS" in os.environ: 
  7.         return int(os.environ["NUM_THREADS"]) 
  8.     if "MKL_NUM_THREADS" in os.environ: 
  9.         return int(os.environ["MKL_NUM_THREADS"]) 
  10.     return multiprocessing.cpu_count() 

接著在 Rust 端,我們可以定義 create_pool。這個(gè)輔助函數(shù)從 num_threads 構(gòu)造一個(gè) Rayon ThreadPool 對(duì)象。

  1. pub fn create_pool(num_threads: usize) -> Result<rayon::ThreadPool, BedErrorPlus> { 
  2.    match rayon::ThreadPoolBuilder::new() 
  3.       .num_threads(num_threads) 
  4.       .build() 
  5.    { 
  6.       Err(e) => Err(e.into()), 
  7.       Ok(pool) => Ok(pool), 
  8.    } 

最后,在 Rust translator 函數(shù) read_f64_py 中,我們從 create_pool(num_threads)?.install(...) 內(nèi)部調(diào)用 read_no_alloc(很好的 Rust 函數(shù))。這將所有 Rayon 函數(shù)限制為我們?cè)O(shè)置的 num_threads。

  1. [...] 
  2.     create_pool(num_threads)?.install(|| { 
  3.         read_no_alloc( 
  4.             filename, 
  5.             [...] 
  6.         ) 
  7.      })?; 
  8. [...] 

規(guī)則 8:將 nice 動(dòng)態(tài)類(lèi)型 Python 函數(shù)翻譯成 nice Rust 泛型函數(shù)

nice Python read 函數(shù)的用戶可以指定返回的 NumPy 數(shù)組的 dtype(int8、float32 或 float64)。從這個(gè)選擇中,該函數(shù)查找適當(dāng)?shù)?Rust translator 函數(shù)(read_i8(_py)、read_f32(_py) 或 read_f64(_py)),然后調(diào)用該函數(shù)。

  1. def read( 
  2.     [...] 
  3.     dtype: Optional[Union[type, str]] = "float32", 
  4.     [...] 
  5.     ) 
  6.     [...] 
  7.     if dtype == np.int8: 
  8.         reader = read_i8 
  9.     elif dtype == np.float64: 
  10.         reader = read_f64 
  11.     elif dtype == np.float32: 
  12.         reader = read_f32 
  13.     else: 
  14.         raise ValueError( 
  15.           f"dtype'{val.dtype}'not known, only" 
  16.           + "'int8', 'float32', and 'float64' are allowed." 
  17.         ) 
  18.      reader( 
  19.        str(self.filepath), 
  20.        [...] 
  21.      ) 

三個(gè) Rust translator 函數(shù)調(diào)用相同的 Rust 函數(shù),即在 src/lib.rs 中定義的 read_no_alloc。以下是 translator 函數(shù) read_64 (又稱(chēng) read_64_py) 的相關(guān)部分:

  1. #[pyfn(m)] 
  2. #[pyo3(name = "read_f64")] 
  3. fn read_f64_py( 
  4.     [...] 
  5.     val: &PyArray2<f64>
  6.     num_threads: usize, 
  7.  ) -> Result<(), PyErr> { 
  8.     [...] 
  9.     let mut val = unsafe { val.as_array_mut() }; 
  10.     [...] 
  11.     read_no_alloc( 
  12.         [...] 
  13.         f64::NAN, 
  14.         &mut val, 
  15.      ) 
  16.      [...] 

我們?cè)?src/lib.rs 中定義了 niceread_no_alloc 函數(shù)。也就是說(shuō),該函數(shù)適用于具有正確特征的任何類(lèi)型的 TOut 。其代碼的相關(guān)部分如下所示:

  1. fn read_no_alloc<TOut: Copy + Default + From<i8> + Debug + Sync + Send>
  2.     filename: &str, 
  3.     [...] 
  4.     missing_value: TOut, 
  5.     val: &mut nd::ArrayViewMut2<'_, TOut>
  6. ) -> Result<(), BedErrorPlus> { 
  7. [...] 

在 nice Python、translator Rust 和 nice Rust 中組織代碼,可以讓我們?yōu)?Python 用戶提供動(dòng)態(tài)類(lèi)型的代碼,同時(shí)仍能用 Rust 編寫(xiě)出漂亮的通用代碼。

規(guī)則 9:創(chuàng)建 Rust 和 Python 測(cè)試

你可能只想編寫(xiě)會(huì)調(diào)用 Rust 的 Python 測(cè)試。但是,你還應(yīng)該編寫(xiě) Rust 測(cè)試。添加 Rust 測(cè)試使你可以交互地運(yùn)行測(cè)試和交互地調(diào)試。Rust 測(cè)試還為你以后得到 Rust 版本的包提供了途徑。在示例項(xiàng)目中,兩組測(cè)試都從 bed_reader/tests/data 讀取測(cè)試文件。

在可行的情況下,我還建議編寫(xiě)函數(shù)的純 Python 版本,然后就可以使用這些慢速 Python 函數(shù)來(lái)測(cè)試快速 Rust 函數(shù)的結(jié)果。

最后,關(guān)于 CI 腳本,例如 bed-reader/ci.yml,應(yīng)該同時(shí)運(yùn)行 Rust 和 Python 測(cè)試。

原文鏈接:https://towardsdatascience.com/nine-rules-for-writing-python-extensions-in-rust-d35ea3a4ec29

【本文是51CTO專(zhuān)欄機(jī)構(gòu)“機(jī)器之心”的原創(chuàng)譯文,微信公眾號(hào)“機(jī)器之心( id: almosthuman2014)”】  

戳這里,看該作者更多好文 

 

責(zé)任編輯:趙寧寧 來(lái)源: 51CTO專(zhuān)欄
相關(guān)推薦

2011-03-31 09:22:56

c++

2011-05-16 13:44:11

C++

2024-09-30 16:25:40

2020-07-29 07:52:25

編程開(kāi)發(fā)IT

2023-09-01 10:43:22

IT外包企業(yè)

2025-04-27 08:06:50

2018-05-28 07:27:18

2023-07-17 11:43:07

2020-07-10 14:25:32

Python編程代碼

2019-07-09 13:42:12

數(shù)據(jù)備份云計(jì)算系統(tǒng)

2011-03-24 12:32:15

數(shù)據(jù)庫(kù)性能優(yōu)化

2010-02-06 09:59:54

C++ void使用規(guī)

2011-08-29 16:05:07

高性能SQL語(yǔ)句SQL Server

2019-09-30 08:00:00

圖數(shù)據(jù)庫(kù)數(shù)據(jù)庫(kù)

2022-10-21 18:41:23

RustC++Azure

2021-04-27 07:52:19

C++promisefuture

2015-12-31 10:00:41

Java日志記錄規(guī)則

2023-06-06 07:17:44

云變化管理策略

2022-02-07 11:24:08

云安全云計(jì)算

2016-10-28 13:21:36

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

欧美壮男野外gaytube| 欧美日韩情趣电影| 日韩一区二区高清| 国产99久久久欧美黑人 | 国产成人高清在线| 国模吧一区二区| 亚洲色成人网站www永久四虎| 免费国偷自产拍精品视频| 91青青草视频| 综合激情视频| 日韩精品视频三区| 中文字幕22页| 黄在线观看免费网站ktv| 国产亚洲一二三区| 亚洲综合精品伊人久久| 亚洲黄色免费观看| 91高清一区| 日韩国产精品一区| 欧美日韩理论片| a一区二区三区| 亚洲综合久久久| 日韩一本精品| 午夜黄色小视频| 国产老妇另类xxxxx| 国产suv精品一区二区三区88区| 四虎影院中文字幕| av一区二区高清| 亚洲国产高潮在线观看| 色啦啦av综合| 视频在线日韩| 欧美日韩国产精品一区| 麻豆一区二区三区在线观看| 番号集在线观看| 97久久人人超碰| 成人av免费电影| 国产又爽又黄免费软件| 日韩成人免费电影| 91精品国产精品| 久久伊人成人网| 91成人免费| 中文字幕免费精品一区| 91网址在线观看精品| 国产精品天堂蜜av在线播放| 色婷婷综合久久久久中文一区二区| 99er在线视频| 污污片在线免费视频| 久久综合久久综合久久综合| 精品国产一区二区三区免费| 亚洲精品网站在线| 国产电影一区二区三区| 国产一区香蕉久久| 亚洲视频在线观看免费视频| 日韩电影免费在线| 国产精品xxx视频| 神马久久久久久久| 久热精品在线| 国产成人精品久久久| 欧美在线观看不卡| 久久午夜精品一区二区| 日本精品久久久久久久| 久久久成人免费视频| 一本一本久久| 啪一啪鲁一鲁2019在线视频| 69成人免费视频| 日韩有码一区二区三区| 国产精品极品美女粉嫩高清在线| 啪啪小视频网站| 免费黄网站欧美| 成人国产精品免费视频| 国产熟女一区二区三区四区| 国产高清精品久久久久| 国产精品久久久久av福利动漫| 国模无码一区二区三区| av激情综合网| 欧美一二三区| 日本精品在线| 一区二区三区久久久| 国产精品久久久久7777| 深夜成人在线| 在线观看国产一区二区| 日韩va在线观看| 日韩精品一级| 精品视频在线播放色网色视频| a级大片在线观看| 99精品在线观看| 欧美大片在线免费观看| 国产精品男女视频| 美女在线视频一区| 国产福利一区二区三区在线观看| 五月婷中文字幕| 中国av一区二区三区| 精品国产三级a∨在线| 激情影院在线| 91福利视频在线| 99视频在线观看视频| 欧美日韩一本| 日韩最新在线视频| 99免费在线观看| 日韩黄色免费网站| 99中文字幕| 国产一级在线观看| 一区二区三区欧美日韩| av免费在线播放网站| 成人免费在线观看视频| 精品日韩欧美一区二区| av电影在线不卡| 欧美久久成人| 国产精品久久久久久久久| www.国产视频| 国产精品色哟哟网站| 男人添女人下部视频免费| 三级成人黄色影院| 精品区一区二区| www.日本高清视频| 亚洲精品人人| 亚洲字幕一区二区| 成年人在线看| 欧美日韩国产综合视频在线观看中文 | 中文字幕 欧美日韩| 欧美一级三级| 欧美裸身视频免费观看| 一级久久久久久| 97久久超碰精品国产| 制服丝袜综合日韩欧美| 日韩电影大全网站| 日韩av在线看| 天堂资源在线播放| 国产黄色精品视频| 亚洲在线播放电影| 欧美日韩尤物久久| 日韩精品高清在线| 中文字幕第28页| 国产精品18久久久久久久久| 亚洲一区二区在线免费观看| 午夜日韩成人影院| 亚洲精品综合精品自拍| 日韩熟女精品一区二区三区| 国产精品中文字幕欧美| 一本一生久久a久久精品综合蜜| 成人爱爱网址| 亚洲精品在线视频| 亚洲欧美精品一区二区三区| 99这里都是精品| r级无码视频在线观看| 免费精品一区| 久久躁狠狠躁夜夜爽| 国产精品久久久久久久一区二区| 欧美国产综合色视频| www.色就是色| 不卡日本视频| 国产精品视频自拍| 调教视频免费在线观看| 欧美色区777第一页| 欧美日韩生活片| 免费在线一区观看| 先锋影音网一区| 国产一区二区精品调教| 一区二区av在线| 中文字幕在线观看1| 国产精品免费看片| 亚洲欧美手机在线| 自由日本语亚洲人高潮| 国产精品av一区| 亚洲风情在线资源| 亚洲亚裔videos黑人hd| 欧美视频xxxx| 一区二区中文字幕在线| 少妇性l交大片7724com| 国产一区欧美| 蜜桃视频在线观看91| 亚洲第一影院| 日韩视频在线免费观看| 国产肥老妇视频| 亚洲第一主播视频| 高潮毛片无遮挡| 麻豆91在线播放免费| 日本三日本三级少妇三级66| 99re8这里有精品热视频8在线| 97在线观看免费| 成年人在线观看网站| 欧美一级免费大片| 久久久久久久久久久久久久av| 久久蜜臀中文字幕| 久久6免费视频| 亚洲午夜黄色| 色大师av一区二区三区| 久久gogo国模啪啪裸体| 91精品国产777在线观看| 国产福利小视频在线| 日韩欧美一级精品久久| 一级片中文字幕| 国产精品成人网| 不许穿内裤随时挨c调教h苏绵| 亚洲国产免费| 亚洲一区免费看| 国产欧美三级电影| 国产精品一区二区三区毛片淫片 | 91地址最新发布| 日韩av中文| 日韩国产在线播放| 99精品久久久久久中文字幕 | 亚洲国产精品久久久久婷蜜芽| 精品九九在线| 国产精品视频福利| 少妇高潮一区二区三区99| 久久久久久久久久久免费精品| 国模吧精品人体gogo| 亚洲二区在线播放视频| 亚洲无码精品国产| 欧美午夜电影在线| 久久亚洲精品大全| 亚洲人妖av一区二区| 亚洲国产日韩一区无码精品久久久| 国产河南妇女毛片精品久久久| 少妇黄色一级片| 亚洲深夜av| 国产 欧美 日本| 99久久精品费精品国产| 欧美亚洲免费高清在线观看| 国产成人在线中文字幕| 91香蕉国产在线观看| 99久久伊人| 国产97色在线|日韩| 91禁在线看| 欧美国产日本在线| av在线影院| 久久久久www| 亚洲成人三级| 亚洲最新视频在线| 日本成人一区二区三区| 亚洲高清久久久久久| 国产高清免费在线观看| 91麻豆精品国产91| 在线观看毛片av| 欧美日韩精品一二三区| 亚洲午夜无码久久久久| 91福利在线看| 欧美激情一区二区三区免费观看| 色婷婷久久99综合精品jk白丝| 国产欧美日韩另类| 亚洲亚洲人成综合网络| 免费中文字幕日韩| 中文字幕日韩精品一区| 中文字幕在线观看免费高清| 久久久精品综合| 波多野结衣影院| www.色综合.com| 噜噜噜在线视频| 成人国产精品免费观看动漫| 精产国品一区二区三区| 国产高清在线精品| 波多野结衣三级视频| 国产尤物一区二区| 性高潮久久久久久| 国产麻豆成人精品| 欧美激情第四页| 国产白丝精品91爽爽久久| 亚洲图片 自拍偷拍| 激情综合网最新| 国产裸体视频网站| 国产成人啪免费观看软件| 99热这里只有精品2| 天堂精品中文字幕在线| 色免费在线视频| 美腿丝袜亚洲综合| 91高清国产视频| 国产成人丝袜美腿| 亚洲图片欧美另类| www.成人网.com| 五月天精品视频| 欧美韩国日本综合| 中文字幕无码日韩专区免费| 一区二区三区不卡视频 | 伊人成综合网| 热99这里只有精品| 国产午夜精品一区二区三区欧美 | 中文字幕电影av| 一区二区视频在线看| 日韩欧美性视频| 动漫精品一区二区| 免费一级a毛片| 欧美一级欧美一级在线播放| 亚洲欧美激情在线观看| 亚洲国产精品一区二区久| av在线免费观看网| 中文字幕精品www乱入免费视频| 日本在线免费看| 91黄色8090| 久久精品97| 99三级在线| 国产在视频线精品视频www666| 亚洲国产一区二区三区在线| 一区二区国产在线| 黄色片视频在线播放| 久久av中文字幕片| 无码人妻丰满熟妇啪啪网站| 国产三级一区二区三区| 国产免费一区二区三区四区| 亚洲大片精品永久免费| 在线播放国产一区| 精品免费日韩av| 九色国产在线观看| 色综合视频一区中文字幕| 成人性生活av| 91亚洲精品久久久| 欧美艳星介绍134位艳星| 国产一区二区三区播放| 久久久久久9| 午夜男人的天堂| 国产精品国产精品国产专区不片| 久久久久久国产精品免费播放| 欧日韩精品视频| 动漫av一区二区三区| 尤物99国产成人精品视频| 国产精品vvv| 成人性生交大片免费观看嘿嘿视频| 精品国产影院| 久久综合亚洲精品| 日韩精品五月天| 中文字幕乱妇无码av在线| 中文字幕亚洲电影| av黄色在线看| 日韩一区二区在线观看视频| 日本不卡视频一区二区| 欧美大片在线影院| 久久久久九九精品影院| 色播五月综合| 久久久噜噜噜| 五月婷婷综合在线观看| 亚洲午夜羞羞片| 999免费视频| 久久久久www| 久久99国产精品二区高清软件| 日韩.欧美.亚洲| 国产精品嫩草99av在线| 激情小说欧美色图| 一区二区三区久久久| 一二三区中文字幕| 一个色综合导航| 日韩伦理一区二区| 日本一区二区视频| 亚洲黄页一区| 国产三级视频网站| 黄色成人av网| 瑟瑟在线观看| 97免费中文视频在线观看| 亚洲精品一区二区三区中文字幕| av电影一区二区三区| 久久国产精品区| 综合 欧美 亚洲日本| 欧美日韩国产欧美日美国产精品| 黄色网址在线播放| 欧美一级大片在线观看| 亚洲精品**不卡在线播he| 欧美视频免费看欧美视频| 99re亚洲国产精品| 日韩精品一区二区不卡| 亚洲成人av在线| 粉嫩一区二区| 欧美三级网色| 美女爽到呻吟久久久久| 老头老太做爰xxx视频| 91搞黄在线观看| 麻豆视频在线免费观看| 91久久国产精品91久久性色| 无需播放器亚洲| 亚洲视频天天射| 婷婷综合久久一区二区三区| 亚洲欧美黄色片| 91精品国产精品| 国产成人黄色| 天堂中文av在线| 一区二区三区.www| 丰满人妻熟女aⅴ一区| 欧美激情精品久久久| 精品丝袜久久| 欧美成人黄色网址| 成人欧美一区二区三区| 内射无码专区久久亚洲| 欧美性在线观看| 欧美中文一区二区| 四川一级毛毛片| 亚洲电影一级黄| 三级黄视频在线观看| 国产精品视频yy9099| 亚洲成人99| 狠狠人妻久久久久久综合蜜桃| 在线免费观看一区| 国产天堂在线| 成人9ⅰ免费影视网站| 久久精品导航| 青娱乐国产盛宴| 亚洲色图25p| 日本免费在线一区| 91专区在线观看| 中文字幕 久热精品 视频在线| 国产综合在线播放| 国产精品成人免费电影| 黄色综合网站| 欧美黄色高清视频|