迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
可迭代对象
list,tuple,str,dict等类型的数据可以使用for ... in ... 的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也加迭代。
自己实现一个可以迭代的对象
# coding=utf-8
import time
from collections import Iterable
from collections import Iterator
class Classmate(object):
def __init__(self):
self.names = list()
self.current_num = 0
def add(self, name):
self.names.append(name)
def __iter__(self):
''' 如果想要一个对象可以迭代,那么必须实现__iter__方法 '''
return self
def __next__(self):
if self.current_num < len(self.names):
ret = self.names[self.current_num]
self.current_num += 1
return ret
else:
raise StopIteration
classmate = Classmate()
classmate.add("老王")
classmate.add("王二")
classmate.add("张三")
for name in classmate:
print name
实现斐波拉契数列
# coding=utf-8
class FibIterator(object):
'''斐波拉契数列迭代器'''
def __init__(self, n):
'''
:param n: int, 指明生成数列的前n个数
'''
self.n = n
# current 保存当前生成到数列中的第几个数
self.current = 0
# num1 保存前前一个数,初始值为数列中的第一个数 0
self.num1 = 0
# num2 保存前一个数,初始值是数列中的第二个数 1
self.num2 = 1
def __next__(self):
''' 被next()函数调用来获取下一个数 '''
if self.current < self.n:
num = self.num1
self.num1, self.num2 = self.num2, sefl.num1+self.num2
self.current += 1
return num
else:
raise StopIteration
def __iter__(self):
''' 迭代器的__iter__返回自身即可 ’‘’
return self
if __name__ == "__main__":
fib = FibIterator(10)
for num in fib:
print num
生成器
利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成。但是我们在实现一个迭代器时,关于当前迭代到的状态需要自己记录,进而才能根据当前状态生成下一个数据。为了达到记录当前状态,并配合next()函数进行迭代使用,我们可以采用更加简便的方法---生成器。
生成器是一种特殊的迭代器
创建生成器方法1
要创建一个生成器,有很多方法。第一种方法,只要把一个列表生成式的[]改为()
L = [x*2 for x in range(5)]
print L
>>> [0, 2, 4, 6, 8]
G = (x*2 for x in range(5))
print G
>>> <generator object <genexpr> at 0x7f626c123db0>
# 创建L和G的区别仅在于最外层的[]和(),L是一个列表,而G是一个生成器。
# 我们可以直接打印列表L中每一个元素,而对于生成器G。我们可以按照迭代器的使用方式来使用。
# 即可以通过next()函数,for循环,list()等方法来使用。
next(G)
>>> 0
next(G)
>>> 2
next(G)
>>> 4
创建生成器方法2
generator 非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以使用函数来实现。
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
# print a
yield a # 如果一个函数中有yield语句,那么就不函数,而是一个生成器模板
a, b = b, a + b
current_num += 1
# 如果在调用 create_num 的时候,发现这个函数中有yield那么此时,不是调用函数,而是创建一个生成器对象。
obj = create_num(10)
print next(obj)
print next(obj)
for num in obj:
print num
# =====================================================
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
yield a
a, b = b, a + b
current_num += 1
return "ok...."
obj2 = create_num(10)
while True:
try:
ret = next(obj2)
print ret
except E xception as ret:
print ret.value # 获取 return 的值
break
# 使用 send ==================================
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
ret = yield a
print ret # 这里会打印 hahahaha
a, b = b, a + b
current_num += 1
return "ok...."
obj3 = create_num(10)
# obj3.send(None) # send 一般不会放到第一次启动生成器,如果非要这么做,那么只能传递 None
ret = next(obj)
print ret
# send 里面的数据会传递给 上面的 ret,当做yield的结果,然后ret保存这个结果
# send 的结果是下一次调用yield时,yield 左边的值
ret = obj,send(“hahahaha”)
print ret
yield 实现多任务
import time
def task_1():
while True:
print "11111"
time.sleep(0.1)
yield
def task_2():
while True:
print "22222"
time.sleep(0.1)
yield
def main():
t1 = task_1()
t2 = task_2()
# 先让t1执行一会,当遇到yield的时候,返回回来
# 再执行t2,当t2遇到yield的时候,再次切换到t1
# 这样 t1/t2/t1/t2 交替执行,最终实现了多任务----协程
while True:
next(t1)
next(t2)
if __nam__ == "__main__":
main()
使用 greenlet
import time
from greenlet import greenlet
def test1():
while True:
print "-----A-----"
gr2.switch()
time.sleep(0.5)
def test2():
while True:
print "-------B------"
gr1.switch()
time.sleep(0.5)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
# 切换到 gr1 中运行
gr1.switch()
gevent 使用
greenlet 已经实现了协程,但是这个是人工切换,太麻烦。Python还有一个更加强大的模块 gevent
其原理是当一个 greenlet 遇到IO(指的是 input, output, 比如网络,文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有 greenlet在运行,而不是等待IO。
import time
import gevent
def f1(n):
for i in range(n):
print gevent.getcurrent(), i
# time.sleep(0.5)
gevent.sleep(0.5)
def f2(n):
for i in range(n):
print gevent.getcurrent(), i
# time.sleep(0.5)
gevent.sleep(0.5)
def f3(n):
for i in range(n):
print gevent.getcurrent(), i
# time.sleep(0.5)
gevent.sleep(0.5)
g1 = gevent.spawn(f1, 5)
g2 = gevent.spawn(f2, 5)
g3 = gevent.spawn(f3, 5)
g1.join()
g2.join()
g3.join()
# ================================================
import time
import gevent
import random
from gevent import monkey
# 有耗时操作时需要
monkey.patch_all() # 将程序中用到的耗时操作代码,换为gevent中自己实现的模块
def corourine_work(coroutine_name):
for i in range(10):
print coroutine_name, i
time.sleep(random.random())
gevent.joinall([
gevent.spawn(coroutine_work, "work1"),
gevent.spawn(coroutine_work, "work2")
])