从C#走进Python迭代器

Posted 车来了

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从C#走进Python迭代器相关的知识,希望对你有一定的参考价值。

  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#的,下集整理。

 

以上是关于从C#走进Python迭代器的主要内容,如果未能解决你的问题,请参考以下文章

C# 使用IENUMERABLE,YIELD

由浅入深,走进Python装饰器-----第一篇:基础

C#迭代重载等

C#实现迭代器

匹夫细说C#:庖丁解牛迭代器,那些藏在幕后的秘密

C# 使用IEnumerable,yield 返回结果,同时使用foreach时,在循环内修改变量的值无效