迭代器
术语解释
迭代:迭代表示在原有已存在的事物上追加新的东西
迭代器:迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问结束,迭代器只能往前不能后退
作用
迭代器的存在可以改变传统数据调用的方式(先存后取,占用大量空间),而是变为存储数据生成的规则,这样极大的减少了对内存的消耗
可迭代对象
可迭代数据类型:
字符串、列表、元组、字典、集合
检查对象是否可迭代
1
2
3
4# 利用isinstance(a, b)判断a对象是否由b类创建,是则返回True,否则返回False
from collections.abc import Iterable
list_name = [1, 2, 3, 4]
result = isinstance(list_name, Iterable)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 判断一个对象是否可迭代(可迭代对象中必须包含__iter__私有方法)
from collections.abc import Iterable
class Classmate(object):
def __init__(self):
pass
def __iter__(self):
# __iter__必须返回一个迭代器对象,此处假设这个类本身就是可迭代的
return self # 此处的self表示返回类本身
def __next__(self):
pass
if __name__ == '__main__':
result = isinstance(Classmate(), Iterable)
print(result)检查对象是否是迭代器
1
2
3
4
5# 利用Iterator判断对象是否是一个迭代器
# 判断一个对象是否是迭代器(迭代器中必须包含__iter___、__next__私有方法)
from collections.abc import Iterator
classmate = Classmate()
result = isinstance(classmate, Iterator)创建可迭代的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36# 判断一个对象是否可迭代(可迭代对象中必须包含__iter__私有方法)
from collections.abc import Iterable
# 判断一个对象是否是迭代器(迭代器中必须包含__iter___、__next__私有方法), 迭代器是一种特殊对象
from collections.abc import Iterator
class Classmate(object):
def __init__(self):
self.list_name = ["zhangsna", "lisi", "wangwu", "zhaoliu", "rwnqi"]
self.count_num = 0
def __iter__(self):
# 此处必须返回一个可迭代的对象
return self
def __next__(self):
# 该方法用于创建迭代循环,变量通过实例属性来实现
if self.count_num < len(self.list_name):
name = self.list_name[self.count_num]
self.count_num += 1
return name
else:
# 当迭代的元素内容穷尽的时候,此时会报错
# 为了避免报错,在迭代器中使用StopIteration方法,可以在越界时停止迭代
raise StopIteration
if __name__ == '__main__':
user = Classmate()
# iter方法可以直接调用可迭代对象中的__iter__方法
classmate = iter(user)
# next方法可以直接调用迭代器中的__next__方法
first_name = next(user)
for name in user:
print(name)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26# 利用迭代实现斐波那契数列
class Fibonacci(object):
def __init__(self, num):
self.all_num = num
self.count_num = 0
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
if self.count_num < self.all_num:
# 由于需要回调参数,所以此处先赋值,这个是迭代器的特点之一
ret = self.a
# 利用元组直接赋值,更快捷
self.a, self.b = self.b, self.a + self.b
self.count_num += 1
# 将刚刚赋值的内容返回
return ret
else:
raise StopIteration
fibo = Fibonacci(500)
for i in fibo:
print(i)
生成器
术语解释
生成器:是一种特殊的迭代器。迭代器的状态需要我们去记录和更新,为了达到记录当前状态并配合next()函数进行迭代使用,而创建了更高级的语法:生成器
创建方法:
直接生成:把一个列表生成式的[]改成()即可(不常用)
1
2
3
4
5
6
7
8
9
10test = (x for x in range(100)) # 此时生成的iter就是一个生成器
# 只要是生成器,就可以调用iter()方法来直接调用生成器中__iter__私方法
iter_name = iter(test)
# 也可以调用next()方法来直接调用生成器中__next__私方法
iter_name = next(test)
# print函数中,前后两个元素之间没有用参数引用,而是用逗号,则表示前后两个元素组成了一个元组
print("迭代器生成的第一个元素为:", iter_name)
# 利用for循环
for num in test:
print(num)利用yield方法生成 (用于理解gevent)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33# 实现斐波那契数列
# 只要函数中有yield,则该函数不再叫函数,而是叫生成器模板
def create_febonacci(num):
a, b = 0, 1
count_num = 0
while count_num < num:
msg = yield a
a, b = b, a + b
count_num += 1
# 生成器调用中,不会有return的值,如果需要return则需要借助异常来捕获
return count_num
# 借用生成器模板,则不是调用函数,而是创建了一个生成器对象
iter_name = create_febonacci(50)
for num in iter_name:
print(num)
# 利用异常捕获生成器中return语句
iter_name = create_febonacci(2)
try:
while True:
num = next(iter_name)
print(num)
except Exception as msg: # 生成器中return的内容会被当作异常返回
msg.value
break
# 除了next方法可以触发迭代,还可以通过send方法触发迭代
iter_name = create_febonacci(5)
# send()的内容会被返回给生成器(可用赋值语句获取),
# send发送有内容的消息时,一定要放在next调用之后,或者先发送None消息
iter_name.send(None)
iter_name.send("message will send to fix")
协程
术语解释
协程:是指将多个任务通过生成器的方式交替执行。它耗费的内存要比多线程、多进程小很多
实现方式
通过yield在死循环中暂停当前执行的程序,等待下一次的调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24# yield不一定需要返回值
import time
def task1():
while True:
print("------------1-------------")
time.sleep(1)
yield # 当生成器执行到这里时,程序会暂停,并且回到主程序中执行下一步
def task2():
while True:
print("------------2-------------")
time.sleep(1)
yield
def main():
t1 = task1()
t2 = task2()
while True:
next(t1)
next(t2)
if __name__ == "__main__":
main()
greenlet实现协程
作用:
在协程中通过next方法、yield来实现协程,这样的代码比较冗余,不便于管理,因此引入了greenlet
安装
greenlet是第三方库,需要手动安装
1
sudo pip install greenlet
greenlet使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29from greenlet import greenlet
import time
g1 = None
g2 = None
def f1(x):
for i in range(x):
# greenlet调用生成器时只能通过【生成器对象.switch(参数)】传递参数
print(greenlet.getcurrent(), i)
time.sleep(1)
g2.switch(4) # 当程序执行到此处时,会直接跳转至g2处
def f2(x):
for i in range(x):
print(greenlet.getcurrent(), i)
time.sleep(1)
g1.switch(4)
def main():
global g1
global g2
g1 = greenlet(f1) # 此处只是创建一个生成器对象,并未执行迭代
g2 = greenlet(f2)
g1.switch(1) # 此处开始跳转至生成器内容中,并进行迭代
if __name__ == '__main__':
main()
gevent实现协程
目的
greenlet使用过程中没有添加协程的部分完全独立出来,因此各代码之间耦合度还是很高,不利于阅读和使用,同时,它并没有完全实现协程的理念(充分利用系统的休眠时间去执行其它的任务),因此在greenlet的基础上又开发处gevent,gevent的优点如下:
- 构建方便,降低各模块之间的耦合度
- 能够自动根据系统的休眠情况去执行其它的任务
安装
gevent是第三方库,需要人工安装
1
sudo pip install gevent
实现原理(便于理解)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25import gevent
from time import sleep
def f1(x):
for i in range(x):
print(i)
gevent.sleep(1)
def f2(x):
for i in range(x):
print(i)
gevent.sleep(1)
def f3(x):
for i in range(x):
print(i)
# gevent无法监听到time中的sleep,只能使用gevent中的sleep,对于其它延时方式也同理
gevent.sleep(1) # 运行到此,监听到有休眠状态,故开始切换到其它任务
g1 = gevent.spawn(f1, 5) # 此处只是创建一个迭代器对象,并未执行
g2 = gevent.spawn(f2, 9) # gevent的迭代器参数直接填写在spawn()方法中
g3 = gevent.spawn(f3, 11)
g1.join() # 此处生成迭代器内容,并开始执行
g2.join()
g3.join()真实环境使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import gevent
from gevent import monkey
import time
# 利用monkey,可以将代码中其它模块的延时方法自动转成gevent对应的延时方法
# 遇到延时方法时使用
monkey.patch_all()
def test(x, modle):
for num in range(x):
print("新增板块:{0},目前处于的阶段:{1}".format(modle, num))
# 当使用gevent时,遇到延时的,需要将一整个模块导入进来,否则不利于monkey辨认
time.sleep(1)
# 使用joinall将所有多任务进协程中
# gevent与greenlet不同,gevent会将所有任务的内容全部执行完,而greenlet执行时遇到调用方提前结束的,则会因为报错而无法继续调用其它执行的任务
gevent.joinall([
gevent.spawn(test, 9, "wolk"),
gevent.spawn(test, 8, "talk"),
gevent.spawn(test, 7, "speak")
])
总结
- 进程是资源分配的单位,线程是操作系统调度的单位
- 进程切换需要的资源很大,效率较低,线程切换需要的资源一般,效率一般,协程切换需要的资源少,效率高。
- 多进程、多线程依据cpu的核数,不一定是并行,而协程位于一个线程中,所以是并发