迭代器与生成器

@[toc]

1. 迭代器

我们以后不会自己去写迭代器,只要学会使用迭代器就可以了。

1.1 迭代器的作用

如果有这样的需求,展示列表中的所有数据。
实现方法:① while+索引+计数器;② 迭代器

<kbd>迭代器</kbd>:对可迭代对象(序列类型,如str/list/tuple/dict/set)中的元素进行逐一获取。表象:具有next方法且每次调用都获取可迭代对象中的元素(从前到后一个一个获取)。

1.2 迭代器的使用

① 列表转换成迭代器方法一:<kbd>v = [1, 2, 3, 4, 5, 6]</kbd>,<kbd>val = iter(v)</kbd>(使用内置函数iter)

# coding:utf-8
v = iter([1, 2, 3, 4, 5, 6])
print(v, type(v))
'''
<list_iterator object at 0x000001EF6A0E95F8> <class 'list_iterator'>
'''

② 列表转换成迭代器方法二:<kbd>v = [1, 2, 3, 4, 5, 6]</kbd>,<kbd>val = v.__ iter__ ()</kbd>(使用__ iter__ 方法)

# coding:utf-8
v = [1, 2, 3, 4, 5, 6]
# val = iter(v)#内部会调用__iter__方法
v2 = v.__iter__()
while True:
    val = v2.__next__()
    print(val)

③ 迭代器获取每个值:反复调用<kbd>val.__ next __()</kbd>(val是迭代器对象)

# coding:utf-8
v = iter([1, 2, 3, 4, 5, 6])
val1 = v.__next__()
val2 = v.__next__()
val3 = v.__next__()
val4 = v.__next__()
val5 = v.__next__()
val6 = v.__next__()
print(val1, val2, val3, val4, val5, val6)
'''
1 2 3 4 5 6
'''
# coding:utf-8
v = iter([1, 2, 3, 4, 5, 6])
while True:
    try:
        val = v.__next__()
        print(val)
    except Exception as e:
        break

④ 直到报错:StopIteration错误

# coding:utf-8
v = iter([1, 2, 3, 4, 5, 6])
while True:
    val = v.__next__()
    print(val)
'''
1
2
3
4
5
6
StopIteration
'''
1.3 for循环与迭代器

for循环的内部使用的是迭代器:

# coding:utf-8
v = ['thanlon', 'kiku']
'''
1. for循环内部会将v1转换成迭代器
2. 内部反复执行“迭代器.__next__()”方法,一个个取值
3. 取完值不报错
'''
for item in v:
    print(item)
'''
thanlon
kiku
'''
1.4 可迭代对象

① 可以被for循环且对象中具有iter()方法,还要返回一个迭代器(或生成器),

v = [1, 2, 3]
result = v.__iter__()  # 有__iter__方法,且返回迭代器对象(result),所以v是迭代器对象
print(result)  # result = <list_iterator object at 0x000001E11CB795C0>

② 可以被for循环

1.5 可迭代对象与迭代器之间关系

可迭代对象可以转换成迭代器。

2. 生成器
2.1 生成器函数

① 函数:

def func():
    return 'thanlon'
func()

② 生成器函数
如何判断是生成器函数:内部是否包含yield

# 生成器函数(内部是否包含yield)
def func():
    print('f1')
    yield 1
    print('f2')
    print('f2')
    yield 2
    print('f3')
    print('f3')
    print('f3')
    yield 3
    print('f4')
    print('f4')
    print('f4')
    print('f4')
    print('f4')
    
# 函数内部代码不会被执行,返回一个生成器对象
v = func()
print(v, type(v))
'''
<generator object func at 0x0000021BC91F5570> <class 'generator'>
'''

生成器是可以被for循环的,一旦开始循环内部代码就会开始执行:

# 生成器函数(内部是否包含yield)
def func():
    print('f1')
    yield 1
    print('f2')
    print('f2')
    yield 2
    print('f3')
    print('f3')
    print('f3')
    yield 3
    print('f4')
    print('f4')
    print('f4')
    print('f4')
    print('f4')

v = func()
for item in v:
    print(item)
'''
f1
1
f2
f2
2
f3
f3
f3
3
f4
f4
f4
f4
f4
'''
2.2 yield from

从当前生成器跳到另一个生成器。

# coding:utf-8
def func():
    yield 3
    yield 4

def func2():
    yield 1
    yield 2
    yield from func()
    yield 5

result = func2()
for item in result:
    print(item)
# coding:utf-8
def func():
    return 3

def func2():
    yield 1
    yield 2
    yield from func()
    yield 5

result = func2()
for item in result:
    print(item)
'''
TypeError: 'int' object is not iterable
'''
# coding:utf-8
# 使用可迭代的列表类型
def func():
    return [3, 4]

def func2():
    yield 1
    yield 2
    yield func()
    yield 5

result = func2()
for item in result:
    print(item)
'''
1
2
[3, 4]
5
'''
# coding:utf-8
def func():
    return [3, 4]

def func2():
    yield 1
    yield 2
    # yield func()  # 把[3, 4]当作整体拿过来
    yield from func()  # 把[3,4]拆开拿过来
    yield 5

result = func2()
for item in result:
    print(item)
'''
1
2
3
4
5
'''
2.3 生成器例子
# coding:utf-8
def func():
    return 1
    if 1 != 1:
        yield 2
        yield 3

v = func()
print(v, type(v))
'''
<generator object func at 0x000001D3974F5570> <class 'generator'>
'''
# coding:utf-8
def func():
    while True:
        yield 1

val = func()
print(val)  # <generator object func at 0x000002CC842C5570>
for item in val:
    print(item)
# coding:utf-8
def func():
    count = 1
    while True:
        yield count
        count += 1

val = func()
print(val)
for item in val:
    print(item)
# coding:utf-8
def func():
    return 1
    yield 2
    yield 3

val = func()
for item in val:
    print(item)  # 由于return的作用,无内容
# coding:utf-8
def func():
    yield 2
    return 111
    yield 3

val = func()
for item in val:
    print(item)
'''
2
'''
# coding:utf-8
def func():
    count = 1
    while True:
        yield count
        count += 1
        if count == 101:
            return
           
val = func()
for item in val:
    print(item)
'''
打印1~100
'''
2.4 生成器总结

函数中如果存在yield(注意与return无关),那么这个函数就是一个生成器函数。调用一个生成器函数会返回一个生成器对象,生成器只有被for循环时,生成器函数内部的代码才会被执行,生成器每次循环都会获取yield返回的值。

2.5 生成器应用示例

① 读取文件案例:分批读取文件,将文件的内容返回给调用者

# coding:utf-8
def func():
    cursor = 0
    while True:
        f = open('log.txt', 'r', encoding='utf-8')
        f.seek(cursor)
        data_list = []
        for i in range(5):  # 每次读5条
            line = f.readline()
            if not line:
                return
            data_list.append(line)
        cursor = f.tell()  # 获取当前游标位置
        f.close()
        for row in data_list:
            yield row

for item in func():
    print(item)

② redis源码示例:
安装第三方模块Redis:<kbd>pip install redis</kbd>

import redis

conn = redis.Redis(host='……')
# scan_iter()是一个生成器函数
conn.scan_iter()
def scan_iter(self, match=None, count=None):
    """
    Make an iterator using the SCAN command so that the client doesn't
    need to remember the cursor position.

    ``match`` allows for filtering the keys by pattern

    ``count`` allows for hint the minimum number of returns
    """
    cursor = '0'
    while cursor != 0:
        # 每次取100条数据
        # cursor:取完之后的游标位置
        # data:本次取出来的100条数据
        cursor, data = self.scan(cursor=cursor, match=match, count=count)
        for item in data:
            yield item
2.6 生成器补充

① 生成器的两个作用:

  • 生成数据
  • 迭代

② 生成器是特殊的迭代器
生成器有next方法:

# coding:utf-8
def func():
    yield 1
    yield 2
    yield 3

v = func()  # v是生成器(对象)
# print(v, type(v))  # <generator object func at 0x0000023986405570> <class 'generator'>
# print(dir(v))  # 查看生成器v中都有哪些方法,dir(v)返回列表
for i in dir(v):
    print(i)
'''
__class__
__del__
__delattr__
__dir__
__doc__
__eq__
__format__
__ge__
__getattribute__
__gt__
__hash__
__init__
__init_subclass__
__iter__
__le__
__lt__
__name__
__ne__
__new__
__next__
__qualname__
__reduce__
__reduce_ex__
__repr__
__setattr__
__sizeof__
__str__
__subclasshook__
close
gi_code
gi_frame
gi_running
gi_yieldfrom
send
throw
'''

且每次调用都获取生成器对象中的元素:

# coding:utf-8
def func():
    yield 1
    yield 2
    yield 3

v = func()  # v是生成器(对象),也是特殊的迭代器对象
result = v.__next__()
print(result)
result = v.__next__()
print(result)
result = v.__next__()
print(result)
'''
1
2
3
'''

③ 生成器是特殊的可迭代对象:生成器中有iter方法

# coding:utf-8
'''
把v当做可迭代对象
'''
def func():
    yield 1
    yield 2
    yield 3

v = func()
result = v.__iter__()
print(result)
'''<generator object func at 0x00000237F7135570>'''

如果一个对象有iter()方法且返回一个迭代器称这个对象是可迭代对象。如果一个对象,它有iter()方法,它返回一个生成器,它也是可迭代对象。

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

推荐阅读更多精彩内容