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])]
|
|
生成器表达式,格式: 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#的,下集整理。