C#與python的迭代器比較: 迭代器 C# Python 一個對象可迭代,需要實現IEnumerable(表示對象可數),IEnumerable就是要實現一個IEnumerator(迭代對象)。 這樣的說法曾經一度讓我很迷,如果返回一個已實現的類似於數組array、列表list類型的IEnume ...
C#與python的迭代器比較:
迭代器 |
|
C# |
Python |
一個對象可迭代,需要實現IEnumerable(表示對象可數),IEnumerable就是要實現一個IEnumerator(迭代對象)。
這樣的說法曾經一度讓我很迷,如果返回一個已實現的類似於數組array、列表list類型的IEnumerator,那實現介面IEnumerable不就很雞肋了? |
學習一個新語言,就會重新認識一些語言特性,這個過程是很有趣的。
Python的迭代器叫Iterator,可迭代就要實現迭代器__iter__(self),和下一項內容__next__(self),這個設計上,顆粒感更強一些。 |
IEnumerator GetEnumerator(); |
__iter__ __next__ 註: 1.可以將__iter__和__next__調用替換為python內置函數iter()和next() 2.在python2中實現的方法名是next,為了相容性,python3中要同時實現next(self) |
public class TestGenerable : IEnumerable { private int n; private int a; private int b; public TestGenerable(int n) { this.n = n; this.a = 1; this.b = 1; } public IEnumerator GetEnumerator() { for (int i = 0; i < n; i++) { yield return a; int t = a; a = b; b = t + b; } } } foreach (var value in new TestGenerable(10)) { WriteLine(value.ToString()); } |
>>> def fbnq(n): ... a, b = 1,1 ... while n > 0: ... yield a ... a, b = b, a+1 ... n -= 1 ... >>> fbnq(1) <generator object fbnq at 0x0000000002D58E08> >>> list(fbnq(3)) [1, 1, 2]
|
狹隘的我竟然從來沒有想一想斐波那契數列更好的實現方式,可以說非常沒有靈魂了。 這個實例非常好的說明瞭yield return 怎麼使用,Generator是個什麼東西。 |
|
|
Tips: 1.Python沒有for(int i; i <n; i++)這樣的迴圈,python風格是使用range(n),像這樣: for i in range(5): pass.. 2.generator是可以傳遞的, iterator = (‘hello’ for i in rang(3)) 註意,加了最外邊的圓括弧就是生成器對象了,當然,最好避免嵌套兩層以上的生成器表達式。 |
補充:
Python的迭代器 |
熱身: >>> class RepeaterIterator: ... def __init__(self,source): ... self.source = source ... def __next__(self): ... return self.source ... >>> repeater = tt('Hello') >>> next(repeater) 'Hello' 使用next(variable)函數, 解釋器會調用variable實現的__next__函數, 所以會有執行內容。 |
然而: >>> for i in repeater: ... print(str(i)) ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'RepeaterIterator' object is not iterable for in迴圈中,解釋器會找in後面對象實現的__iter__, 沒有實現解釋器會認為其不可迭代,於是報錯; 可以補充一下: class Repeater: ... def __init__(self, value1): ... self.value1 = value1 ... def __iter__(self): ... return RepeaterIterator(self) ... >>> class RepeaterIterator: ... def __init__(self,source): ... self.source = source ... def __next__(self): ... return self.source.value1 ... >>> test = Repeater('hello') >>> a = 1 >>> for i in test: ... if a < 5: ... print(str(i)) ... a += 1 ... else: ... break ... hello hello hello hello |
上例看著還是挺“複雜”的, 其實iter和next可以寫在同一個類裡面: class Repeater: ... def __init__(self, value1): ... self.value1 = value1 ... def __iter__(self): ... return self ... def __next__(self): ... return self.value |
配合語法糖yield服用: >>> def repeater(value): ... while True: ... yield value |
>>> a = repeater('hi') >>> a <generator object repeater at 0x0000000002DC1E60> 只要使用了yield來返回項的,解釋器認為這是個generator類型; 它和iterator差不多,只不是概念上的區別。 generator是“生成器”, 它的下一項更趨向於經過了複雜的計算處理而出來, 而iterator更“輕”一些。 |
序列過濾(查詢) |
|
C# |
Python |
LINQ (本質是使用lambda表達式) |
list切片 lambda 生成器表達式 |
懶得寫 |
切片: 精髓就一句: >>> lst = [1,2,3,4,5] >>> lst[-2::-1] [4, 3, 2, 1] >>> lst[-2::-2] [4, 2] list是個序列,a:b:c, a表示第幾個開始,加-號表示倒數數起; c表示取數跨度,加了-號表示序列反向。
切片,目前我體驗來說, 僅 lst[-1] 表示“取最後一項”是香的; 有些硬用切片進行數據篩選,比較非人哉: dataSet[nonzero(dataSet[:,feature] > value)[0],:] (康康這啥玩意 %#@$%#@$%4@!!) 要從最裡面的方括弧開始看,[:,feature]取所有行的下標為feature的列(輸出n行1列的數組),如果數組元素大於value,對應位置為true否則為false; Nonzero結果第一行是入參非0元素的行位置(python的0等價false,1等價true) 最後取dataset中feature列上值大於value的所有行。
用對象,用lambda就不香了? |
lambda和內置函數filter一起用,就比較LINQ思想了, 下例,以取第4列大於3的所有行: >>> c array([[1, 2, 3, 5, 0], [0, 1, 2, 1, 1]]) >>> list(filter(lambda line:line[3]>3, c[:,:])) [array([1, 2, 3, 5, 0])] 如果需要轉換為numpy.array類型,可以這樣處理: >>> np.array(list(ex)) |
|
生成器表達式,格式: genexpr = (expression for item in collection if condition) [註意,最外一定要有圓括弧] [expression是item輸出處理] >>> c array([[1, 2, 3, 5, 0], [0, 1, 2, 1, 1]]) >>> ex = (line for line in c[:,:] if line[3] > 3) >>> for i in ex: ... print(i) ... [1 2 3 5 0] 如果需要轉換為numpy.array類型,可以這樣處理 >>> np.array(list(ex)) 註:generator是單向不可逆的,next()後就釋放當前項了。 |
|
疑問:C#linq靈魂的鏈式方法(拓展方法),在python是怎麼表現的呢 |
python的變數適用範圍,python的裝飾器,此類都是大區別與C#的,下集整理。