迭代器
迭代是访问元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有元素被访问结束。迭代器只能往前不能后退
-
可迭代对象
如果想要一个对象称为可迭代的对象,那么必须实现__Iter__
方法 -
迭代器
如果想要一个对象称为可迭代的对象,那么必须实现__Iter__
方法和__next__
方法
for循环,list,dict,tuple的本质就是迭代器循环:
- 先调用对象的
__iter__
方法,将其变成一个迭代器对象 - 调用
__next__
(迭代器),将得到的返回值赋值给变量名 - 循环往复直到
__next__
(迭代器)抛出异常,for会自动捕捉异常然后结束循环
例如:
from collections.abc import Iterator
from collections.abc import Iterable
class Animals:
def __init__(self):
self.animal = []
self.num = 0
def add(self, name):
self.animal.append(name)
def __iter__(self):
return self
def __next__(self):
if self.num < len(self.animal):
result = self.animal[self.num]
self.num += 1
return result
else:
raise StopIteration
animals = Animals()
animals.add('dog')
animals.add('cat')
animals.add('fish')
print(isinstance(animals, Iterable)) # 确认该类是不是可迭代对象
print(isinstance(animals, Iterator)) # 确认该类是不是迭代器
for i in animals:
print(i)
这里定义的Animals就是一个迭代器,它可以使用for循环进行迭代输出
迭代器的好处
替代器可以是一种生成列表的方法,而不会占用大量的内存空间去存放数据,而是生成这种数据的方法。
例如生成斐波那契数组,我们没必要把大量的数组放在一个列表,而是定义一个生成斐波那契数组的方法,使用的时候就可以直接迭代调用
还有在python2中的range()
方法,它是直接生成了列表,在python3中的range()
方法是一个迭代器,不会直接生成对应的列表,有效解决了数组占用大量空间的问题
生成器
- 生成器是一类特殊的迭代器
如果一个函数里有yield
语句,那么这个就不是函数了,而是一个生成器的模板,当去调用这个的时候,发现这个函数中有yield
,那么此时就不是在调用函数,而是创建了一个生成器对象
例如:
def create_num():
a = 1
while True:
yield a
a += 1
if a == 10:
break
obj = create_num() # 创建生成器对象
resault = next(obj)
print(resault)
resault = next(obj)
print(resault)
print("-" * 10)
for i in obj:
print(i)
打印结果:
这里创建的create_num
就是一个生成器,当我们调用next
方法时,它就会从yield
中返回一个值,然后下一次调用next
方法时,程序会继续从yield
之后继续运行,而不是从头开始运行
send
我们除了可以用next()
函数来唤醒生成器之外,我们也可以使用send()
函数来唤醒,而且send()
还可以在唤醒的时候在断点处传入一个值,前提是在yield前要有一个接受这个参数的值。obj.send(None)
等价next(obj)
使用yield完成多任务
例如:
from time import sleep
def test1():
while True:
print(1)
sleep(0.1)
yield
def test2():
while True:
print(2)
sleep(0.1)
yield
def main():
t1 = test1()
t2 = test2()
while True:
next(t1)
next(t2)
if __name__ == '__main__':
main()
前面我们说了,在函数中添加yield
就会变成一个生成器,当调用next()
方法时就会在yield
出暂停,下次调用next
会在暂停处继续执行,这么这两个函数就会交替执行,即并发执行
使用gevent完成多任务
为了更好的使用协程来实现多任务,python中的greenlet
模块对其封装,大致原理就是使用switch()
在两个函数间来回切换。
但是,greenlet
还是得人工切换,所以python中还有一个比greenlet更强大且能够自动切换任务的模块gevent
-
原理:当一个函数中出现阻塞或者耗时的时候,gevent就会自动利用阻塞或者耗时的这段时间去切换到下一个函数去执行,直到阻塞或耗时结束,再在合适的时候切换回来继续执行
-
那怎么才能让gevent直到这是阻塞呢?
比如延时sleep
就得使用gevent.sleep()
,socket中的阻塞也得使用gevent中的阻塞,这无疑是对代码的重构,这当然是不行的,所以要给程序打补丁
在程序的前面先从gevent中导出monkey,然后执行一下monkey.patch_all()
就行了,他会把程序中的则色和延时自动替换成gevent的。
例如:
import time
# from time import time
import gevent
from gevent import monkey
monkey.patch_all() # 将程序中用到的耗时操作的代码,换成gevent中自己实现的模块
def test1():
while True:
print(1)
time.sleep(0.1)
def test2():
while True:
print(2)
time.sleep(0.1)
def main():
gevent.joinall([
gevent.spawn(test1),
gevent.spawn(test2),
])
if __name__ == '__main__':
main()
- 注意这里有一个坑,就是耗时操作不能直接将函数从模块中导出来,例如使用time中的sleep,之前我们都是习惯
from time import time
,然后后面直接使用sleep(x)
,但在这里不行,只能是import time
之后在使用time.sleep(x)
,这样monkey.patch_all()
才能将程序中用到的耗时操作的代码,换成gevent中自己实现的模块。
1 条评论
python一直都想学来着