Python Cookbook学习笔记ch4_01

第四章迭代器与生成器

敢点我给你好看查看效果更好噢

4.1手动遍历迭代器

  • 问题:想要遍历一个可迭代对象,不使用for循环
  • 方案:手动遍历,使用next()函数
def manual_iter():
    with open('data_file/test1_3.txt') as f:
        try:
            while True:
                line = next(f)
                print(line,end=' ')
        except StopIteration:
            pass
manual_iter()
my name is flfl
 love python 
 say hello
  to the world
  python nihao 
 yongyuan 
 yanthon
 pythonnn 
  • 通常StopIteration用来指示迭代的结尾,但是也可以通过返回指定值来指示结尾
def manual_iter():
    with open('data_file/test1_3.txt') as f:
        while True:
            line = next(f,None)
            if line is None:
                break
            print(line,end=' ') 
manual_iter()
my name is flfl
 love python 
 say hello
  to the world
  python nihao 
 yongyuan 
 yanthon
 pythonnn 
items = [1,2,3]
it = iter(items)
next(it)
1
next(it)
2
next(it)
3
next(it)
---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

 in ()
----> 1 next(it)


StopIteration: 

4.2代理迭代

  • 问题:自定义了一个可迭代的容器对象,如何进行迭代操作
  • 方案:定义一个__iter__()方法,将迭代操作代理到容器内部的对象上即可
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)
root = Node(0)
child1 = Node(1)
child2 = Node(2)
root.add_child(child1)
root.add_child(child2)
for ch in root:
    print(ch)
Node(1)
Node(2)

4.3使用生成器创建新的迭代模式

  • 问题:想要实现一个自定义迭代模式,跟普通的内置函数range(),reversed()不一样
  • 方案:使用生成器函数来定义
def frange(start,stop,increment):
    x = start
    while x < stop:
        yield x
        x += increment
for n in frange(0,4,0.5):
    print(n,end=' , ')
0 , 0.5 , 1.0 , 1.5 , 2.0 , 2.5 , 3.0 , 3.5 , 
list(frange(0,1,0.125))
[0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875]
  • 实验:用于展示其工作机制
def countdown(n):
    print("开始计数值:",n)
    while n>0:
        yield n
        n -= 1
    print('结束')
c = countdown(3)
c

next(c)
开始计数值: 3





3
next(c)
2
next(c)
1
next(c)
结束



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

 in ()
----> 1 next(c)


StopIteration: 

4.4实现迭代器协议

  • 问题:如何构建一个能支持迭代操作的自定义的对象
  • 方案:使用生成器函数
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)
    def depth_first(self):
        yield self
        for c in self:
            yield from c.depth_first()
root  = Node(0)
child1 = Node(1)
child2 = Node(2)
root.add_child(child1)
root.add_child(child2)
child1.add_child(Node(3))
child1.add_child(Node(4))
child2.add_child(Node(5))
for ch in root.depth_first():
    print(ch)
Node(0)
Node(1)
Node(3)
Node(4)
Node(2)
Node(5)
  • python 的迭代协议要求一个__iter__()的方法返回一个特殊的迭代器对象,这个迭代器对象实现了__next__()方法。但是实现这些比较繁琐,下面使用并联迭代器类重新实现depth_first()方法
class Node2:
    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)
    def depth_first(self):
        return DepthFirstIterator(self)

class DepthFirstIterator(object):
    def __init__():
        self._node = start_node
        self.children_iter = None
        self._child_iter = None
    def __iter__(self):
        return self
    def __next__():
        if self._children_iter is None:
            self._children_iter = iter(self._node)
            return self._node
        elif self._child_iter:
            try:
                nextchild = next(self._child_iter)
                return nextchild
            except StopIteration:
                self._child_iter = None
                return next(self)
        else:
            self._child_iter = next(self._children_iter).depth_first()
            return next(self)

4.5反向迭代

  • 问题:反向迭代一个序列
  • 方案:使用内置的reversed()函数
a = [1,2,3,4]
list(reversed(a))
[4, 3, 2, 1]
for x in reversed(a):
    print(x,end=' ')
4 3 2 1 
  • 反向迭代器仅当对象的大小可以预先确定或者对象实现了__reversed__()方法。如果都不满足,必须先将对象转化为一个list才可以
with open('data_file/test1_3.txt') as f:
    for line in reversed(f):
        print(line)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
      1 with open('data_file/test1_3.txt') as f:
----> 2     for line in reversed(f):
      3         print(line)


TypeError: '_io.TextIOWrapper' object is not reversible
with open('data_file/test1_3.txt') as f:
    for line in reversed(list(f)):
        print(line,end=' ')
 python nihao 
  to the world
 say hello
 love python 
 my name is flfl
  • 但是如果元素很多,list一个对象很占内存。为此可以自定义类上实现reversed方法来实现反向迭代
class Countdown:
    def __init__(self,start):
        self.start = start
    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1
    def __reversed__(self):
        n = 1
        while n < self.start:
            yield n
            n += 1

for rr in reversed(Countdown(5)):
    print(rr,end=' ')
print()
for rr in Countdown(10):
    print(rr,end=' ')
1 2 3 4 
10 9 8 7 6 5 4 3 2 1 

4.6带有外部状态的生成器函数

  • 问题:如何定义一个生成器函数,它可以调用某个你想暴露给用户使用的外部状态值
  • 方案:将它实现为一个类,然后放入到生成器函数__iter__()中
from collections import deque
class linehistory:
    def __init__(self,lines, histlen=3):
        self.lines = lines
        self.history = deque(maxlen=histlen)
    def __iter__(self):
        for lineno,line in enumerate(self.lines,1):
            self.history.append((lineno,line))
            yield line
    def clear(self):
        self.history.clear()
with open ('data_file/test1_3.txt') as f:
    lines = linehistory(f)
    for line in lines:
        if 'python' in line:
            for lineno, hline in lines.history:
                print('{}:{}'.format(lineno,hline),end=' ')
1:my name is flfl
 2:love python 
 3:say hello
 4: to the world
 5: python nihao 
  • 注意:如果在迭代器中不使用for循环语句,那么得先调用iter()函数
with open('data_file/test1_3.txt') as f:
    lines = linehistory(f)
    next(lines)
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
      1 with open('data_file/test1_3.txt') as f:
      2     lines = linehistory(f)
----> 3     next(lines)


TypeError: 'linehistory' object is not an iterator
with open('data_file/test1_3.txt') as f:
    lines = linehistory(f)
    it = iter(lines)
    print(next(it))
my name is flfl

4.7迭代器切片

  • 问题:标准的切片不能使用
  • 方案:使用itertools.islice()
def count(n):
    i = 0
    while iyield i
        i += 1

c = count(10)
c[1:5]
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

 in ()
      6 
      7 c = count(10)
----> 8 c[1:5]


TypeError: 'generator' object is not subscriptable
import itertools
for x in itertools.islice(c,1,5):
    print(x,end=' ')
1 2 3 4 
c = list(count(10))
c[1:5]
[1, 2, 3, 4]

你可能感兴趣的:(Python,Cookbook)