Iterator:迭代器对象,必须要实现 next 魔法函数
Iterable:可迭代对象,继承 Iterator,必须要实现 iter 魔法函数
迭代器 (Iterator) 与可迭代对象 (Iterable) 的区别:
-
可迭代对象:继承迭代器对象,可以用 for 循环(说明实现了 iter 方法)
-
迭代器对象:可以用 next 获取下一个值(说明实现了 next 方法),但是每个值只能获取一次,单纯的迭代器没有实现 iter 魔法函数,所以不能使用 for 循环
-
只要可以用作 for 循环的都是可迭代对象
-
只要可以用 next() 函数的都是迭代器对象
-
列表,字典,字符串是可迭代对象但是不是迭代器对象,如果想变成迭代器对象可以使用 iter() 进行转换
-
Python 的 for 循环本质上是使用 next() 进行不断调用,for 循环的是可迭代对象,可迭代对象中有 iter 魔法函数,可迭代对象继承迭代器对象,迭代器对象中有 next 魔法函数
-
一般由可迭代对象变迭代器对象
生成器
生成器:函数中只要有 yield,这个函数就会变成生成器。每次运行到 yield 的时候,函数会暂停,并且保存当前的运行状态,返回返回当前的数值,并在下一次执行 next 方法的时候,又从当前位置继续往下走。
def gen():
yield 1
# 返回一个对象,这个对象的值是1
def ret():
return 1
# 返回一个数字1
g = gen()
r = ret()
print(g,r)
print(next(g))
<generator object gen at 0x000001487FDA2D58> 1
1
可以看到return是直接返回数值 1,yield 是返回的一个生成器对象,这个对象的值是 1,使用 next(g) 或者 for x in g:print x 都是可以获取到他的内容的,这个对象是在 python 编译字节码的时候就产生。
def gen():
yield 1
yield 11
yield 111
yield 1111
yield 11111
yield 111111
# 返回一个对象,这个对象内的值是1和11,111...
def ret():
return 1
return 3
# 第二个return是无效的
g = gen()
r = ret()
print(g,r)
print(next(g))
for x in g:
print(x)
返回结果:
<generator object gen at 0x000002885FE32D58> 1
1
11
111
1111
11111
111111
就像迭代器的特性一样,获取过一遍的值是没法再获取一次的,并且不是那种一次把所有的结果求出放在内存或者说不是一次性读取所有的内容放在内存中。
梳理特性:
使用 yield 的函数都是生成器函数
可以使用 for 循环获取值,也可以使用 next 获取生成器函数的值
应用:
读取文件,使用 open(‘xxx’).read(2019)// 打开一个文件,每次读取 2019 个偏移量。文件 a.txt 是一行文字,但是特别长,这一行文字根据|符号分开,如何读取?
写入文件代码:
# -*- coding:utf-8 -*-
import random
import threading
import string
import time
t1 = time.time()
def write(x):
with open('a.txt','a+')as a:
a.write(x + '||')
def run():
for x in range(10000000):
strs = str(random.randint(1000,2000)) +random.choice(string.ascii_letters)*10
write(strs)
for x in range(10):
t = threading.Thread(target=run)
t.start()
t2 = time.time()
print(t2 - t1)
读取文件代码:
# -*- coding:utf-8 -*-
def readbooks(f, newline):
# f为传入的文件名,newline为分隔符
buf = ""
# 缓存,处理已经读出来的数据量
while 1:
while newline in buf:
# 缓存中的数据是否存在分隔符
pos = buf.index(newline)
# 如果存在就找到字符的位置,比如0或者1或者2
yield buf[:pos]
# 暂停函数,返回缓存中的从头到字符的位置
buf = buf[pos + len(newline):]
# 缓存变成了,字符的位置到末尾
chunk = f.read(2010 * 10)
# 读取2010*10的字符
if not chunk:
# 已经读取到了文件结尾
yield buf
break
buf += chunk
# 加到缓存
with open('a.txt','r')as f:
for line in readbooks(f,'||'):
print(line)