【从C#走进Python】二、迭代器

   日期:2024-01-17     浏览:49    评论:0    

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

 

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服