Python - 迭代器and生成器

迭代器

  • 迭代器不能回退,只能如过河卒子一样,不断前进
  • 迭代器也不适合在多线程中对可变集合使用
  • 特性:一个对象,只能循环一次,若再读就没有任何结果了(游标已经移动到了最后)。

仿写range() 的对象

class MyRange(object):
    def __init__(self,n):
        self.i = 0
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()
if __name__ == "__main__":
    x  = MyRange(7)
    print(x.__next__())
    print(x.__next__())
    for i in x:
        print (i)
0
1
2
3
4
5
6

分析:

  • __iter__() 是类的核心,返回了迭代器本身,实现了__iter__()方法的对象,以为着其可以迭代
  • 含有__next__()的对象就是迭代器,并且在这个方法中,在没有元素的时候发起StopIteration()异常
class Fibs:
    def __init__(self,max):
        self.max = max
        self.a = 0
        self.b = 1
    def __iter__(self):
        return self
    def __next__(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration()
        self.a, self.b = self.b , self.a + self.b
        return fib
fib = Fibs(1000)
print(list(fib))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]

斐波那契数列 中大于1000的最小数

class Fibs_max1000:
    def __init__(self,min):
        self.min = min
        self.a = 0
        self.b = 1
    def __iter__(self):
        return self
    def __next__(self):
        fib = self.a
        if self.a > self.min:
            raise StopIteration()
        self.a, self.b = self.b , self.a + self.b        
        return self.a
fib = Fibs_max1000(1000)
print(list(fib))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597]

Python 2 中的 range() 和 xrange()

  • 都是内建函数
  • range() 返回的是一个列表
  • xrange()返回的是一个对象,类似range(),但不是列表
  • 循环的时候 xrange()比range()稍快,并有更高的内存效率
  • xrange()是可以迭代的,它返回的是一个可迭代对象。
  • range()是得到的列表会一次性被读入内存,而xrange()返回的对象,则需要一个数值才能返回一个数值

Python3 中只有range() ,和Python2 中的xrange()是一样的可以迭代的。

range(10000)
range(0, 10000)

生成器(generator)

  • 生成器必须是可迭代的,但又不完全等同于迭代器
  • 把解析中的‘【】’ 换成‘()’ -> 生成器解析式/生成器推导式/生成器表达式
  • 用途:
    • 代替列表解析当针对大数据的时候,Python处理列表时,将全部数据读入到内存中,而迭代器的优势在于可以只将所需的读入内存。因此生成器解析式比列表解析式少占内存。
  • yield :
    • 含有yield关键字的函数是一个生成器类型对象,这个生成器对象是可迭代的。
    • 调用的时候返回相应的值。
    • 不用使用__iter__()__next__(),只要使用yield 就可以使其具备迭代器的功能特性
  • 一个函数中包含yield语句,它就是生成器,也是迭代器。这种方式比前面写迭代器要简便很多,但不是意味着就可以抛弃迭代器,至于用迭代器和生成器就要视具体情况而定了。
#生成器
my_generator = (x*x for x in range(4))
for i in my_generator:
    print(i)
0
1
4
9

for i in my_generator:
    print(i)

游标已经到最末尾,所以没有数据了,只能前进不能倒退。


#列表
my_list = [x*x for x in range(4)]
for i in my_list:
    print(i)
0
1
4
9

for i in my_list:
    print(i)
0
1
4
9

用途示例:

#生成器
sum(x*x for x in range(10))
285

#列表
sum([x*x for x in range(10)])
285

yield

def g():
    yield 0
    yield 1
    yield 2
    yield 3
ge = g() #返回生成器
type(ge)
generator

ge.__next__() #开始执行,遇到第一个yield将值返回,并暂停执行(挂起)
0

ge.__next__() #从上次暂停位置开始,向下执行,遇到yield,将值返回,又挂起
1

ge.__next__() #重复上一步
2

ge.__next__() #重复上一步
3

ge.__next__() #从上面挂起的位置开始,但是后美女没有可执行的了,于是 __next__异常
---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-59-fcae21b194e6> in <module>()
----> 1 ge.__next__() #从上面挂起的位置开始,但是后美女没有可执行的了,于是 __next__异常


StopIteration: 

yield 与 return

  • 一般函数都止于 return
  • 作为生成器,有了yield 只是让程序在内部挂起,如果再之后还有return,遇到return就直接抛出SoptIteration异常而终止迭代
def r_return(n):
    print('You taked me')
    while n > 0:
        print('before return')
        return n
        n -= 1
        print('after return')
rr = r_return(3)
You taked me
before return

rr  #retuen 后面的语句不会执行
3

def y_yield(n):
    print('You taked me')
    while n > 0:
        print('before yield')
        yield n
        n -= 1
        print('after yield')
yy = y_yield(3) #没哟执行函数体语句
yy.__next__()   #开始执行,遇到 yield 返回值,并暂停
You taked me
before yield





3

yy.__next__()  #从上次位置开始 ,遇到 yield 返回值,并暂停
after yield
before yield





2

yy.__next__() #从上次位置开始 ,遇到 yield 返回值,并暂停
after yield
before yield





1

yy.__next__()  #没有满足条件的值,抛出异常
after yield



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

StopIteration                             Traceback (most recent call last)

<ipython-input-65-86493d8df2a1> in <module>()
----> 1 yy.__next__()  #没有满足条件的值,抛出异常


StopIteration: 

yield 与 return 相遇

def y_yield(n):
    print('You taked me')
    while n > 0:
        print('before yield')
        yield n
        n -= 1
        print('after yield')
        return n
yy = y_yield(3) #没哟执行函数体语句
yy.__next__()   #开始执行,遇到 yield 返回值,并暂停
You taked me
before yield





3

yy.__next__()   #开始执行,遇到 yield 返回值,并暂停
after yield



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

StopIteration                             Traceback (most recent call last)

<ipython-input-67-e2663c79b073> in <module>()
----> 1 yy.__next__()   #开始执行,遇到 yield 返回值,并暂停


StopIteration: 2

在斐波那契数列中用上 diely

#diely 实现
def fibs(max):
    n,a,b = 0,0,1
    while n < max:
        yield b
        a,b = b,a+b
        n = n+1

if __name__ == "__main__":
    f = fibs(10)
    for i in f:
        print(i)
1
1
2
3
5
8
13
21
34
55

#迭代器实现
class Fibs:
    def __init__(self,max):
        self.max = max
        self.a = 0
        self.b = 1
    def __iter__(self):
        return self
    def __next__(self):
        fib = self.a
        if fib > self.max:
            raise StopIteration()
        self.a, self.b = self.b , self.a + self.b
        return fib
fib = Fibs(50)
print(list(fib))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# list  实现
def fib_1(n):
    result = [0,1]
    for i in range(n-2):
        result.append(result[-2] + result[-1])
    return result
lst = fib_1(10)
print(lst)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# 递归 实现
def fib_2(n):
    if n==0:
        return 0
    elif n==1:
        return 1
    else:
        return fib_2(n-1) + fib_2(n-2)
f = fib_2(10)
for i in range(10):
    print(fib_2(i))
    
0
1
1
2
3
5
8
13
21
34

生成器方法

  • Python 2.5 以后生成器就有了一个新特性,就是在开始运行后能够为生成器提供新的值。,这就好似生成器和‘外界’之间进行数据交流
  • send()
    • send() 方法只能在 生成器运行并被挂起后才能使用,即 yield 至少执行一次,如果没有执行,就会报错
    • 如果 send(None) 的话,就会返回上次输入的值
  • close()
    • 不用调用参数,用来关闭生成器
  • throw()
    • throw(type,value = None,traceback = None)
    • 用于在生成器内部(生成器的当前挂起处或者未启动时的定义处)抛出一个异常(在yield表达式中)
def repeater(n):
    while True:
        n = (yield n)

r = repeater(4)
r.__next__() #生成器开始执行,遇到 yield 内部挂起。
4

r.send("hello") # 被挂起的程序,被唤醒,返回 send 发送的值 这就是在运行后能够为生成器提供值的含义。
'hello'

help(r.send)
Help on built-in function send:

send(...) method of builtins.generator instance
    send(arg) -> send 'arg' into generator,
    return next yielded value or raise StopIteration.

r.__next__()  # 犹豫没有给参数,所以 yield 就只能返回None了
# 没有运行起来且挂起的生产器,直接send 就会报错。
r_1 = repeater(5)
r_1.send("how")
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-77-1289d56d86b5> in <module>()
      1 # 没有运行起来且挂起的生产器,直接send 就会报错。
      2 r_1 = repeater(5)
----> 3 r_1.send("how")


TypeError: can't send non-None value to a just-started generator
r_1.send(None) #如果传None 就会返回上一个输入的值
5

参考:《跟着老齐学Python:从入门到精通》 作者:齐伟 电子工业出版社

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,578评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,701评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,691评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,974评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,694评论 6 413
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,026评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,015评论 3 450
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,193评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,719评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,442评论 3 360
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,668评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,151评论 5 365
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,846评论 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,255评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,592评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,394评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,635评论 2 380