函数 -- 迭代器与生成器

迭代
  • 我们可以通过for循环来遍历listtuple,这种遍历称为迭代(Iteration)
>>> list = ['Google', 'Neuedu', 'Taobao', 'Baidu']
>>> for x in list:
        pass
>>> tup = ('Google', 'Neuedu', 'Taobao')
>>> for x in tup:
...     pass
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
...     print(x, y)
  • for循环不仅可以用在listtuple上,只要是可迭代对象,都可以迭代。
>>> dict = {'a': 1, 'b': 2, 'c': 3}
>>> for key in dict :  # 默认情况下迭代的是key
...     pass

>>> for value in dict.values() :  # 迭代value
...     pass

>>> for k, v in dict.items() :  # 同时迭代key和value
...     pass
>>> for x in 'abcdef':
...     pass 
  • 判断是否为可迭代对象,使用isinstance函数,将对象collections模块的Iterable类型进行比较来判断
>>> from collections import Iterable
>>> isinstance('abc', Iterable)
True
>>> isinstance([1,2,3], Iterable)
True
>>> isinstance(123, Iterable)
False
  • 练习:使用迭代查找一个list中最小和最大值,并返回一个tuple:
def findMinAndMax(L):
    return (None, None)
# 测试
if findMinAndMax([]) != (None, None):
    print('测试失败!')
elif findMinAndMax([7]) != (7, 7):
    print('测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
    print('测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
    print('测试失败!')
else:
    print('测试成功!')
迭代器
  • 迭代器是一个可以记住元素遍历位置的对象。
  • 迭代器有两个基本函数:iter()创建迭代器,next()迭代下一个元素。
  • 字符串列表元组字典集合都可用于创建迭代器:
  • 迭代器只能从集合的第一个元素开始访问,直到所有元素被访问完。
>>> list = [1, 2, 3]
>>> it = iter(list)     # 创建迭代器对象
>>> print( next(it) )   # 输出迭代器的下一个元素
1
>>> print( next(it) )
2
>>> print( next(it) )
3
>>> print( next(it) )  # 没有元素可迭代时报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
  • 迭代器对象通常使用for语句进行遍历:
>>> list = [1,2,3,4]
>>> it = iter(list)    # 创建迭代器对象
>>> for x in it:
...     print (x, end=" ")

1 2 3 4
列表生成式

一种用来创建list的表达式。

使用range()函数可以生成有序的数列列表

>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

要生成[1x1, 2x2, 3x3, ..., 10x10]怎么办,可以使用循环+range

>>> L = []
>>> for x in range(1, 11):
...    L.append(x * x)

但这种循环形式太繁琐,可以使用列表生成式来生成

>>> [ x * x for x in range(1, 11) ]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

编写列表生成式时,把要生成的元素x * x放到前面,后面跟for循环,就可以把list创建出来

  • for循环后面还可以加上if判断,对元素进行筛选
>>> [ x * x  for x in range(1, 11)  if x % 2 == 0 ]
[4, 16, 36, 64, 100]
  • for循环中同时迭代两个变量来生成list
>>> d = { 'x': 'A',  'y': 'B',  'z': 'C' }
>>> [ k + '=' + v  for k, v  in d.items() ]
['y=B', 'x=A', 'z=C']
  • 使用双层循环,可以生成全排列
>>> [m + n  for m in 'ABC'  for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
  • 可以调用函数调用方法来生成list
# 列出当前目录下的所有文件和目录名
>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
# 把一个list中所有的字符串变成小写
>>> L = [ 'Hello',  'World',  'IBM',  'Apple' ]
>>> [ s.lower()  for s in L ]
['hello', 'world', 'ibm', 'apple']
  • 练习:添加if语句保证列表生成式能正确地执行
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = ???

# 测试:
print(L2)
if L2 == ['hello', 'world', 'apple']:
    print('测试通过!')
else:
    print('测试失败!')
生成器
  • 通过列表生成式可以直接创建列表。但受到内存限制,列表容量是有限的。而且对于包含了100万个元素的列表,如果仅访问前面几个元素,那么后面的空间都被浪费了。
  • 如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,而在循环过程中不断推算出后续的元素,从而节省大量的空间。
  • Python中将这种边循环边计算的机制,称为生成器(generator)
创建生成器
  • 创建 生成器(generator),只要把列表生成式[]改成()即可。
>>> g = ( x * x for x in range(10) )  # 创建生成器
>>> type(g)
<class 'generator'>

创建生成器后,可以调用next()函数获得生成器的下一个返回值,因为生成器中保存的是算法,每次调用next(g)时就会计算下一个元素的值,直到没有更多元素时,抛出StopIteration的错误。

>>> next(g)
0
>>> next(g)
1

更通用的方法是使用for循环来迭代它,并且不需要关心StopIteration错误。

>>> g = ( x * x for x in range(10) )
>>> for n in g:
...     print(n)

当函数定义中包含yield时,函数将变成生成器

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
  • odd不是普通函数,而是生成器,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)时报错。
  • 一定要给循环设置退出条件,否则会产生无限数列。

如果生成列表的算法比较复杂,无法使用列表生成式时,可以用函数来实现。

创建生成器的另一种方法,先定义函数,再将print(b)改为yield b

  • 斐波那契数列(Fibonacci)为例,
  • 定义函数
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

>>> type(fib)
<class 'function'>
  • print替换为yield
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b           # 将print改为yield
        a, b = b, a + b
        n = n + 1
    return 'done'

>>> f = fib(6)
>>> type(f)
<class 'generator'>
>>> f = fib(6)
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)
8
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: done
  • 使用for循环迭代生成器
>>> for x in fib(6):
...     print(x)
...
1
1
2
3
5
8
生成器执行过程
  • 生成器函数的执行流程不一样。
  • 函数是顺序执行,遇到return语句或最后一行语句时返回。
  • 生成器是在每次调用next()时候执行,遇到yield语句时返回,当再次调用next()时,从上次返回的yield语句处继续执行。
可迭代对象和迭代器
  • 可以直接作用于for循环的对象称为可迭代对象(Iterable)
  • 可以被next()函数调用,并返回下一个值的对象称为迭代器(Iterator)
  • 生成器(generator)迭代器(Iterator)
  • listdictstr等是可迭代对象(Iterable)但不是迭代器(Iterator)
  • 迭代器(Iterator)计算是 惰性 的,只有需要返回下一个数据时才会计算
  • 练习:把每行看做一个list,编写生成器输出下一行杨辉三角形
          1
         / \
        1   1
       / \ / \
      1   2   1
     / \ / \ / \
    1   3   3   1
   / \ / \ / \ / \
  1   4   6   4   1
 / \ / \ / \ / \ / \
1   5   10  10  5   1
def triangles():
    pass
# 期待输出:
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]
# [1, 5, 10, 10, 5, 1]
# [1, 6, 15, 20, 15, 6, 1]
# [1, 7, 21, 35, 35, 21, 7, 1]
# [1, 8, 28, 56, 70, 56, 28, 8, 1]
# [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
n = 0
results = []
for t in triangles():
    print(t)
    results.append(t)
    n = n + 1
    if n == 10:
        break
if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')




- end -

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

推荐阅读更多精彩内容