zoukankan      html  css  js  c++  java
  • Python 进阶_迭代器 & 列表解析

    目录

    迭代器

    迭代器是一个含有 next() 方法的对象,让我们可以迭代不是序列数据类型但表现出序列行为的对象,所以可以说迭代器为类序列对象提供了一个类序列的接口(只要是实现了 __iter__() 方法的对象,就可以使用迭代器来进行访问)。迭代器从对象的第一个元素开始访问,直到所有的元素被遍历后结束。对于无法通过索引计数来随机访问元素的数据结构(EG. set)而言,迭代器是唯一的访问其自身元素的方式。

    NOTE1: 但迭代器是不支持索引计数的,所以迭代器不能回退,只能往前进行迭代。

    NOTE2: 迭代器也不是线程安全的,在多线程环境中对可变对象使用迭代器是一个危险的操作。所以一般情况下应该坚持对不可变对象实现迭代器。

    NOTE3: 迭代器对象不支持被多次迭代

    In [19]: a = ListIter([1,2,3,4,5])
    
    In [20]: [i for i in a]
    Out[20]: [1, 2, 3, 4, 5]
    
    In [21]: [i for i in a]
    Out[21]: []
    
    In [22]:

    iter() :内建的迭代器生成函数

    iter(…)
    iter(collection) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.
    

    1. 如果传递了一个序列(sequence)实参,迭代器会从索引 0 一直迭代至结束。
    2. 如果传递了两个实参 EG. iter(func, sentinel),迭代器会重复的调用 func 直到迭代器的下一个值为 sentinel 。

    EXAMPLE 1

    In [44]: aList = ['jmilkfan', 'fanguiju', 'chocolate']
    
    In [45]: aIter = iter(aList)
    
    In [46]: aIter.next()
    Out[46]: 'jmilkfan'
    
    In [47]: aIter.next()
    Out[47]: 'fanguiju'
    
    In [48]: next(aIter)
    Out[48]: 'chocolate'
    
    In [49]: aIter.next()
    ---------------------------------------------------------------------------
    StopIteration                             Traceback (most recent call last)
    <ipython-input-49-5ab62b0e2847> in <module>()
    ----> 1 aIter.next()
    
    StopIteration:

    迭代器通过其内建的 iter.next() 方法,或通过 Python 内建的 next() 来迭代下一个元素,直到最后触发 StopIteration 异常后表示迭代结束。

    EXAMPLE 2: 捕获异常

    In [56]: aList = ['jmilkfan', 'fanguiju', 'chocolate']
    
    In [57]: aIter = iter(aList)
    
    In [58]: while True:
        ...:     try:
        ...:         print aIter.next()
        ...:     except StopIteration:
        ...:         print 'Done'
        ...:         break
        ...:
    jmilkfan
    fanguiju
    chocolate
    Done

    迭代器在 for 循环中

    EXAMPLE 3: 对 EXAMPLE 2 的改进

    In [59]:  aList = ['jmilkfan', 'fanguiju', 'chocolate']
    
    In [60]:  aIter = iter(aList)
    
    In [61]: for x in aIter:
        ...:     print x
        ...:
    jmilkfan
    fanguiju
    chocolate

    Python 在 for 循环的语法糖中,让 for 循环能够自动的调用迭代器的 next() 方法以及捕获 StopIteration 异常。

    迭代器与字典

    字典是一个可迭代对象,其迭代器会变量它的 key, 所以我们可以应用这个特性将语句 for eachKey in myDict.keys() 改进为 for eachKey in myDict
    EXAMPLE 4:

    In [62]: aDict = {'name':'jmilkfan', 'sex':'man'}
    
    In [69]: for x in aDict:
        ...:     print ''.join([x,': ',aDict[x]])
        ...:
    name: jmilkfan
    sex: man

    迭代器与文件

    文件也是一个可迭代对象,迭代器会自动的调用文件对象的 readline() 方法,所以可以将语句 for eachLine in myFile.readlines() 修改为 for eachLine in myFile
    EXAMPLE 5:

    myFile = open('FILENAME')
    
    for eachLine in myFile:
        print eachLine
    
    myFile.close()

    创建迭代器对象

    EXAMPLE 6: 一个斐波那契数列

    class Fab(object): 
       def __init__(self, max): 
           self.max = max 
           self.n, self.a, self.b = 0, 0, 1 
    
       def __iter__(self): 
           return self 
    
       def next(self): 
           if self.n < self.max: 
               r = self.b 
               self.a, self.b = self.b, self.a + self.b 
               self.n = self.n + 1 
               return r 
           raise StopIteration()
    
    if __name__ == '__main__':
        fab = Fab(5)
        print fab
        for x in fab:
            print x

    Output:

    In [79]: run demo_1.py
    <__main__.Fab object at 0x00000000047CDE80>
    1
    1
    2
    3
    5

    将实例化语句 fab = Fab(5) 修改成 fab = Fab(100) 后执行,再看看 Output:

    1
    .
    .
    .
    354224848179261915075

    NOTE: 这一类的应用场景会对内存造成非常大的负担,建议使用迭代器来减少内存的压力。

    EXAMPLE 6 中的 __iter__() 方法 return 了自己,所以迭代的对象就是自身。除此之外,我们还可以实现 委托迭代

    创建迭代对象并实现委托迭代

    委托迭代:就是将迭代请求委托到迭代对象内部持有的容器对象上。它能让自己创建的新容器能够完成迭代操作。
    EXAMPLE 7

    class Node:                   
        def __init__(self, value):
            self._value = value
            self._children = []            # 迭代对象所持有的容器对象
    
        def __repr__(self):
            return 'Node({!r})'.format(self._value)
    
        def add_child(self, node):
            self._children.append(node)
    
        def __iter__(self):
            return iter(self._children)      # 将迭代请求转发给迭代对象内部所持有的容器对象
    
    if __name__ == '__main__':
        root = Node(0)
        child1 = Node(1)
        child2 = Node(2)
        root.add_child(child1)
        root.add_child(child2)
        for ch in root:   # 调用 Node:__iter__()
            print(ch)     # 调用 Node:__repr__()

    Output:

    Node(1)
    Node(2)

    EXAMPLE 7 是一个树结构,执行自身(root),返回子节点(Node(1)、Node(2)) 。这个例子,当我们 for 循环遍历迭代器对象 root 时,实际上是迭代了 self._children = [Node(1), Node(2)],这就是迭代委托。

    迭代器的多次迭代

    在上文写到,迭代器不支持被多次迭代,这样实在不能说是灵活。为了解决这一个问题,引入了可迭代对象(iterables)迭代器对象(iterator)两个不同的概念。

    • 迭代器对象__iter__() 返回的是迭代器对象自身。
    • 可迭代对象__iter__() 返回了一个迭代器对象。

    上述的 委托迭代 就是一个返回一个迭代器对象的例子,所以 EXAMPLE 7 是一个 可迭代对象,他能够被多次迭代。

    In [1]: run demo_1.py
    Node(1)
    Node(2)
    
    In [2]: run demo_1.py
    Node(1)
    Node(2)

    列表解析

    是一个非常有用、简单且灵活的工具,让我们能够动态的创建列表类型对象。
    语法

    [expr for iter_variable in iterable]

    EXAMPLE 1

    In [18]: [x**2 for x in range(10)]
    Out[18]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    
    In [80]: map(lambda x:x ** 2, range(10))
    Out[80]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    上面两条语句的效果是一样的,但列表解析的方法仅调用一次 range(10), 而第二条则调用了 map()/lambda/range(10),这说明列表解析可以替代 map() 以及 lambda ,以此来获取更高的效率

    列表解析的样例

    • 嵌套 for 循环实现的矩阵
    In [81]: [(x+1, y+1) for x in range(3) for y in range(5)]
    Out[81]:
    [(1, 1),
     (1, 2),
     (1, 3),
     (1, 4),
     (1, 5),
     (2, 1),
     (2, 2),
     (2, 3),
     (2, 4),
     (2, 5),
     (3, 1),
     (3, 2),
     (3, 3),
     (3, 4),
     (3, 5)]
    • 统计文件单词数
    f = opem('FILENAME', 'r')
    len([word for line in f for word in line.split()])
    • 求素数
    [x for x in range(2,100) if not [y for y in range(2,int(x/2+1)) if x % y == 0]]
    • 嵌套列表降维
    In [143]: nestLi = [[1,2,3],[4,5,6],[7,8,9]]
    
    In [144]: newLi = [x for para in nestLi for x in para]
    
    In [145]: newLi
    Out[145]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

    列表解析和迭代器

    因为 for 循环的关系,所以从上面的例子可以看出列表解析和迭代器之间的关系非常紧密。深入的理解两者,对提高 Python 程序的效率有非常大的帮助。迭代是 Python 一个非常重要的思想和特性。

  • 相关阅读:
    Net学习日记_SQL_1
    Net学习日记_基础提高_11_俄罗斯方块_代码篇
    Net学习日记_基础提高_11_俄罗斯方块_整理概括篇
    Net学习日记_基础提高_10
    C#抽象类和接口
    RSS大全
    如何使用deparam.js抓参数
    h5页面解决软键盘与100%X100%的页面的冲突
    29、数据库三大范式精要总结
    28、数据库三大范式精讲
  • 原文地址:https://www.cnblogs.com/jmilkfan-fanguiju/p/7532310.html
Copyright © 2011-2022 走看看