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

  • 相关阅读:
    Python爬虫入门一之综述
    关于 PHP 7 你必须知道的五件事
    10个用于Web开发的最好 Python 框架
    如何用Python编写一个聊天室
    无需操作系统直接运行 Python 代码
    使用Python开发chrome插件
    Python下用Scrapy和MongoDB构建爬虫系统(1)
    python 目录操作
    用主题模型可视化分析911新闻(Python版)
    React Native通信机制详解
  • 原文地址:https://www.cnblogs.com/carmen-019/p/13252148.html
Copyright © 2020-2023  润新知