rust python对比_Python Rust 迭代器对比
迭代是數(shù)據(jù)處理的基石,而 Python 中所有集合都可以迭代,這是 Python 讓使用者感到非常方便的特征之一。
下面是一些在 Python 中經(jīng)常使用的迭代模式
# 列表
for i in [1, 2, 3, 4]:
print(i)
# 字典
di = {'a': 1, 'b': 2, 'c': 3}
# 迭代鍵
for k in di.keys():
print(k)
# 迭代鍵值
for k, v in di.items():
print('{}: {}'.format(k, v))
除了基本數(shù)據(jù)類型,Python 也支持為自定義的數(shù)據(jù)類型實(shí)現(xiàn)迭代器協(xié)議。Python 解釋器在需要迭代對(duì)象 x 時(shí)會(huì)自動(dòng)調(diào)用 iter(x)。
內(nèi)置的 iter 函數(shù)有如下作用。
檢查對(duì)象是否實(shí)現(xiàn)了 __iter__ 方法,如果實(shí)現(xiàn)了就調(diào)用它,__iter__ 方法返回一個(gè)迭代器
如果沒有實(shí)現(xiàn) __iter__ 方法,但是實(shí)現(xiàn)了 __getitem__ 方法,Python 會(huì)創(chuàng)建一個(gè)迭代器,嘗試按順序(從索引0)獲取元素。
如果上述兩個(gè)嘗試失敗,Python 會(huì)拋出 TypeError 異常,提示該元素不可迭代。
所以如果我們要讓某個(gè)對(duì)象是可迭代對(duì)象,只需要實(shí)現(xiàn) __iter__,這個(gè)方法要求返回一個(gè)迭代器,那什么是迭代器呢? Python 中標(biāo)準(zhǔn)的迭代器接口有兩個(gè)方法。
__next__
返回下一個(gè)可用的元素,如果元素,拋出 StopIteration 異常。
__iter__
返回迭代器自身,即 self,以便在應(yīng)該使用可迭代對(duì)象的地方使用迭代器,如 for 循環(huán)中。
這里需要說明的一點(diǎn)是,可迭代對(duì)象與迭代器是不同的,《流暢的 Python》這樣定義可迭代對(duì)象
> 使用 iter 內(nèi)置函數(shù)可以獲取迭代器的對(duì)象。如果對(duì)象實(shí)現(xiàn)了能返回迭代器的 iter 方法,那么對(duì)象就是可迭代的。序列都可以迭代;實(shí)現(xiàn)了 getitem 方法,而且其參 數(shù)是從零開始的索引,這種對(duì)象也可以迭代
>
而迭代器則定義為
> 迭代器是這樣的對(duì)象:實(shí)現(xiàn)了無(wú)參數(shù)的 next 方法,返回序列中的下一個(gè)元素;如 果沒有元素了,那么拋出 StopIteration 異常。Python 中的迭代器還實(shí)現(xiàn)了 iter 方 法,因此迭代器也可以迭代。
也就是說每次對(duì)可迭代對(duì)象調(diào)用 iter(x) 都將返回一個(gè)新的迭代器。
那如果為一個(gè)可迭代對(duì)象實(shí)現(xiàn) __next__ 方法,即把這個(gè)可迭代對(duì)象變成自身的可迭代對(duì)象會(huì)怎樣呢?沒人阻止你這樣做,但當(dāng)你真正為這個(gè)對(duì)象實(shí)現(xiàn)這兩個(gè)方法時(shí),你會(huì)發(fā)現(xiàn)麻煩不斷。舉個(gè)例子
class MyData:
def __init__(self, values):
# 假設(shè) value 為列表
self.values = values
def __iter__(self):
return self
def __next__(self):
# ???
raise NotImplementedError()
按照協(xié)議 __next__ 應(yīng)該返回下一個(gè)元素或者拋出 StopIteration,顯然我們需要一個(gè)屬性存儲(chǔ)當(dāng)前迭代位置,所以應(yīng)該似乎應(yīng)該這樣寫
class MyData:
def __init__(self, values):
self.values = values
# 記錄當(dāng)前迭代位置
self.current = 0
def __iter__(self):
# 每次調(diào)用重頭開始迭代
self.current = 0
return self
def __next__(self):
if self.current < len(self.values):
value = self.values[self.current]
self.current += 1
return value
else:
raise StopIteration
但考慮這樣一種情況,我們調(diào)用2次 iter,交替迭代獲得的2個(gè)迭代器,預(yù)期行為應(yīng)該是2個(gè)迭代器不會(huì)干涉,但如果按上述代碼實(shí)現(xiàn) MyData 對(duì)象行為并不符合預(yù)期。
data = MyData([1, 2, 3, 4, 5])
data_iter1 = iter(data)
print(next(data_iter1)) # 結(jié)果為1
print(next(data_iter1)) # 結(jié)果為2
data_iter2 = iter(data)
print(next(data_iter2)) # 結(jié)果為1
print(next(data_iter1)) # 預(yù)期為3,但得到2
如果把 current 屬性變?yōu)榱斜?#xff0c;每次調(diào)用 iter 增加一個(gè)元素表示新的迭代器當(dāng)前位置呢?但又會(huì)導(dǎo)致 __next__ 變得非常復(fù)雜,因?yàn)樗仨氄业讲煌鲗?duì)應(yīng)當(dāng)前位置,這樣才能保證正確的迭代行為。為什么我們的迭代實(shí)現(xiàn)如此復(fù)雜呢?根本原因在于 __iter__ 總是返回自身,換言之,調(diào)用 iter 的迭代器都是一樣,這其實(shí)破壞了 每次調(diào)用 iter 返回新的迭代器 這一設(shè)計(jì)。
解決難題辦法很簡(jiǎn)單,遵循設(shè)計(jì),把可迭代對(duì)象和迭代器拆開。
class MyData:
def __init__(self, values):
self.values = values
def __iter__(self):
return DataIterator(list(self.values))
class DataIterator:
def __init__(self, values):
self.values = values
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current < len(self.values):
value = self.values[self.current]
self.current += 1
return value
else:
raise StopIteration
現(xiàn)在 __iter__ 將會(huì)返回新的迭代器,每個(gè)迭代器都保存著自身狀態(tài),這讓我們不必費(fèi)心費(fèi)力第維護(hù)迭代器狀態(tài)。
所以,把可迭代對(duì)象變成其自身的迭代器是條歧路,反設(shè)計(jì)的。
在 Rust 中,迭代也遵循著相似的設(shè)計(jì),Rust 中實(shí)現(xiàn)了 Iterator 特性的結(jié)構(gòu)體就被認(rèn)為是可迭代的。
我們可以像 Python 那樣使用 for 循環(huán)迭代
let v1 = vec![1, 2, 3, 4, 5];
for item in v1 {
println!("{}", item);
}
std::iter::Iterator 只要求實(shí)現(xiàn) next 方法即可,下面是一個(gè)官方文檔中的例子
// 首先定義一個(gè)結(jié)構(gòu)體,作為“迭代器”
struct Counter {
count: usize,
}
// 實(shí)現(xiàn)靜態(tài)方法 new,相當(dāng)于構(gòu)造函數(shù)
// 這個(gè)方法不是必須的,但可以讓我更加方便
// 地使用 Counter
impl Counter {
fn new() -> Counter {
Counter { count: 0 }
}
}
// 實(shí)現(xiàn) Iterator 特性
impl Iterator for Counter {
// 確定迭代器的返回值類型
type Item = usize;
// 只有 next() 是必須實(shí)現(xiàn)的方法
// Option 也可以寫成 Option<:item>
fn next(&mut self) -> Option {
// 增加計(jì)數(shù)
self.count += 1;
// 到 5 就返回 :)
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
let mut counter = Counter::new();
let x = counter.next().unwrap();
println!("{}", x);
let x = counter.next().unwrap();
println!("{}", x);
let x = counter.next().unwrap();
println!("{}", x);
let x = counter.next().unwrap();
println!("{}", x);
let x = counter.next().unwrap();
println!("{}", x);
與 for 循環(huán)使用時(shí),Python 使用 StopIteration 告訴編譯是時(shí)候定制循環(huán)了,在 Rust 則是 None,所以 next 方法返回值為 Option<:item>。其實(shí)使用 for 循環(huán)是一種語(yǔ)法糖
let values = vec![1, 2, 3, 4, 5];
for x in values {
println!("{}", x);
}
去掉語(yǔ)法糖后相當(dāng)于
let values = vec![1, 2, 3, 4, 5];
{
let result = match IntoIterator::into_iter(values) {
mut iter => loop {
let next;
match iter.next() {
Some(val) => next = val,
None => break,
};
let x = next;
let () = { println!("{}", x); };
},
};
result
}
編譯器會(huì)對(duì) values 調(diào)用 into_iter 方法,獲取迭代器,接著匹配迭代器,一次又一次地調(diào)用迭代器的 next 方法,直到返回 None,這時(shí)候終止循環(huán),迭代結(jié)束。
這里又涉及到另一個(gè)特性 std::iter::IntoIterator,這個(gè)特性可以把某些東西變成一個(gè)迭代器。
IntoInterator 聲明如下:
pub trait IntoIterator
where
<:intoiter as iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
類比于 Python 中的概念,可以做出以下結(jié)論:
實(shí)現(xiàn)了 IntoIterator 特性的結(jié)構(gòu)體是一個(gè)“可迭代對(duì)象”
實(shí)現(xiàn)了 Iterator 特性的結(jié)構(gòu)體一個(gè)“迭代器”
for 循環(huán)會(huì)嘗試調(diào)用結(jié)構(gòu)的 into_iter 獲得一個(gè)新的“迭代器”,當(dāng)?shù)鞣祷?None 時(shí)提示迭代結(jié)束
基于以上結(jié)論,我們可以實(shí)現(xiàn) Python 例子中類似的代碼
#[derive(Clone)]
struct MyData{
values: Vec,
}
struct DataIterator {
current: usize,
data: Vec,
}
impl DataIterator {
fn new(values: Vec) -> DataIterator {
DataIterator {
current: 0,
data: values
}
}
}
impl Iterator for DataIterator {
type Item = i32;
fn next(&mut self) -> Option {
if self.current < self.data.len() {
let ret = Some(self.data[self.current]);
self.current += 1;
ret
} else {
None
}
}
}
impl IntoIterator for MyData {
type Item = i32;
type IntoIter = DataIterator;
fn into_iter(self) -> DataIterator {
DataIterator::new(self.values)
}
}
fn main() {
let data = MyData { values: vec![1, 2, 3, 4] };
for item in data {
println!("{}", item);
}
}
總結(jié)
Rust 不愧是一門多范式的現(xiàn)代編程語(yǔ)言,如果你之前對(duì)某個(gè)語(yǔ)言有相當(dāng)深入的了解,在學(xué)習(xí) Rust 是總會(huì)有“喔,這不是xxx嗎”的感覺。雖然之前閱讀過 《流暢的Python》,但在可迭代對(duì)象與迭代器這一章并沒有太多影響,因?yàn)樵谑褂?Python 時(shí)真正要我實(shí)現(xiàn)迭代接口的場(chǎng)景非常少;直到最近學(xué)習(xí) Rust,在嘗試使用 Rust 的 Iterator 特性為我的結(jié)構(gòu)實(shí)現(xiàn)與 for 循環(huán)交互時(shí)被 Iterator 和 IntoInterator 特性高的有些蒙圈。最后是靠著 Python 和 Rust 相互對(duì)比,弄清迭代器與可迭代對(duì)象的區(qū)別后才感覺自己真正弄懂了迭代這一重要特性。
延申閱讀
《流暢的Python》 - 第14章,可迭代的對(duì)象、迭代器和生成器
《Rust 編程之道》 6.3 迭代器
總結(jié)
以上是生活随笔為你收集整理的rust python对比_Python Rust 迭代器对比的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 好多人跟我推荐VIDAA电视,这是什么电
- 下一篇: 达梦数据库查询数据库所有表名_达梦数据库