迭代的概念
使用for循环遍历取值的过程叫做迭代,比如:使用for循环遍历列表获取值的过程
# 例如
for value in [2, 3, 4]:
print(value)
可迭代对象
使用for循环遍历取值的对象叫做可迭代对象, 比如:列表、元组、字典、集合、range、字符串
如何判断是否为可迭代对象
# 元组,列表,字典,字符串,集合,range都是可迭代对象
from collections.abc import Iterable
# 3.7以上版本为导入collections.abc
# from collections import Iterable
# 判断对象是否是指定类型
result = isinstance((3, 5), Iterable)
print("元组是否是可迭代对象:", result)
result = isinstance([3, 5], Iterable)
print("列表是否是可迭代对象:", result)
result = isinstance({"name": "张三"}, Iterable)
print("字典是否是可迭代对象:", result)
result = isinstance("hello", Iterable)
print("字符串是否是可迭代对象:", result)
result = isinstance({3, 5}, Iterable)
print("集合是否是可迭代对象:", result)
result = isinstance(range(5), Iterable)
print("range是否是可迭代对象:", result)
result = isinstance(5, Iterable)
print("整数是否是可迭代对象:", result)
# 提示: 以后还根据对象判断是否是其它类型,比如以后可以判断函数里面的参数是否是自己想要的类型
result = isinstance(5, int)
print("整数是否是int类型对象:", result)
class Student(object):
pass
stu = Student()
result = isinstance(stu, Iterable)
print("stu是否是可迭代对象:", result)
result = isinstance(stu, Student)
print("stu是否是Student类型的对象:", result)
自定义可迭代对象
自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
from collections.abc import Iterable
# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):
def __init__(self):
self.my_list = list()
# 添加指定元素
def append_item(self, item):
self.my_list.append(item)
def __iter__(self):
# 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
pass
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
result = isinstance(my_list, Iterable)
print(result)
for value in my_list:
print(value)
执行结果
Traceback (most recent call last):
True
File "/home/python/Desktop/MyIterable.py", line 24, in <module>
for value in my_list:
TypeError: iter() returned non-iterator of type 'NoneType'
自定义迭代器对象
from collections.abc import Iterable
from collections.abc import Iterator
# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):
def __init__(self):
self.my_list = list()
# 添加指定元素
def append_item(self, item):
self.my_list.append(item)
def __iter__(self):
# 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
my_iterator = MyIterator(self.my_list)
return my_iterator
# 自定义迭代器对象: 在类里面定义__iter__和__next__方法创建的对象就是迭代器对象
class MyIterator(object):
def __init__(self, my_list):
self.my_list = my_list
# 记录当前获取数据的下标
self.current_index = 0
# 判断当前对象是否是迭代器
result = isinstance(self, Iterator)
print("MyIterator创建的对象是否是迭代器:", result)
def __iter__(self):
return self
# 获取迭代器中下一个值
def __next__(self):
if self.current_index < len(self.my_list):
self.current_index += 1
return self.my_list[self.current_index - 1]
else:
# 数据取完了,需要抛出一个停止迭代的异常
raise StopIteration
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
result = isinstance(my_list, Iterable)
print(result)
for value in my_list:
print(value)
执行结果:
True
MyIterator创建的对象是否是迭代器: True
1
2
iter函数: 获取可迭代对象的迭代器,会调用可迭代对象身上的__iter__方法
next函数: 获取迭代器中下一个值,会调用迭代器对象身上的__next__方法
# 自定义可迭代对象: 在类里面定义__iter__方法创建的对象就是可迭代对象
class MyList(object):
def __init__(self):
self.my_list = list()
# 添加指定元素
def append_item(self, item):
self.my_list.append(item)
def __iter__(self):
# 可迭代对象的本质:遍历可迭代对象的时候其实获取的是可迭代对象的迭代器, 然后通过迭代器获取对象中的数据
my_iterator = MyIterator(self.my_list)
return my_iterator
# 自定义迭代器对象: 在类里面定义__iter__和__next__方法创建的对象就是迭代器对象
# 迭代器是记录当前数据的位置以便获取下一个位置的值
class MyIterator(object):
def __init__(self, my_list):
self.my_list = my_list
# 记录当前获取数据的下标
self.current_index = 0
def __iter__(self):
return self
# 获取迭代器中下一个值
def __next__(self):
if self.current_index < len(self.my_list):
self.current_index += 1
return self.my_list[self.current_index - 1]
else:
# 数据取完了,需要抛出一个停止迭代的异常
raise StopIteration
# 创建了一个自定义的可迭代对象
my_list = MyList()
my_list.append_item(1)
my_list.append_item(2)
# 获取可迭代对象的迭代器
my_iterator = iter(my_list)
print(my_iterator)
# 获取迭代器中下一个值
# value = next(my_iterator)
# print(value)
# 循环通过迭代器获取数据
while True:
try:
value = next(my_iterator)
print(value)
except StopIteration as e:
break
for循环的本质
遍历的是可迭代对象
for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
遍历的是迭代器
for item in Iterator 循环的迭代器,不断调用next()方法来获取下一个值并将其赋值给item,当遇到StopIteration的异常后循环结束。
from collections.abc import Iterable
from collections.abc import Iterator
'''
判断是否是可迭代对象:
1.0:能用for循环遍历的对象
2.0:可以使用 isinstance() 判断一个对象是否是 Iterable 对象:
'''
'''
可迭代对象的本质就是可以向我们提供一个这样的中间“人”即迭代器帮助我们对其进行迭代遍历使用。
可迭代对象通过__iter__方法向我们提供一个迭代器,
我们在迭代一个可迭代对象的时候,
实际上就是先获取该对象提供的一个迭代器,
然后通过这个迭代器来依次获取对象中的每一个数据.
一个类中只要具有__iter__(self)方法,那么该对象就是一个可迭代对象
'''
class myList(object):
def __init__(self,n):
self.n = n
def __iter__(self):
return MyIterable(self.n)
class MyIterable(object):
def __init__(self,n):
self.n = n
self.current = 0
def __iter__(self):
return self
def __next__(self):
if self.current<self.n:
num = self.current
self.current+=1
return num
else:
raise StopIteration
# test = MyIterable(20)
test = myList(20)
print(isinstance(test,Iterable))
print(isinstance(test,Iterator))
print(isinstance(MyIterable(10),Iterator))
# list、tuple等都是可迭代对象,我们可以通过iter()函数获取这些可迭代对象的迭代器。
# 然后我们可以对获取到的迭代器不断使用next()函数来获取下一条数据。
# iter()函数实际上就是调用了可迭代对象的__iter__方法
# thisIterable = myList()
# print(isinstance([],Iterable))
# print(isinstance([],Iterator))
# print(isinstance(thisIterable,Iterable))
# print(isinstance(thisIterable,Iterator))
# print(isinstance(MyIterable,Iterable))
# print(isinstance(MyIterable,Iterator))
# li = [ i for i in range(5)]
# print(type(li))
# my_list = iter(li)
# print(next(my_list))
生成器的创建方法
# 第一种方法:
# test = (i for i in range(10))
# print(type(test))
# 在def函数里面看到有yield关键字那么就是生成器
# 第二种方法
def myIterable(n):
current = 0
while current < n:
num = current
current += 1
yield num
test = myIterable(5)
for i in range(5):
print(next(test))
def fibonacci(num):
a = 0
b = 1
# 记录生成fibonacci数字的下标
current_index = 0
print("--11---")
while current_index < num:
result = a
a, b = b, a + b
current_index += 1
print("--22---")
# 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
yield result
print("--33---")
fib = fibonacci(5)
value = next(fib)
print(value)
value = next(fib)
print(value)
value = next(fib)
print(value)
# for value in fib:
# print(value)
# 在使用生成器实现的方式中,我们将原本在迭代器__next__方法中实现的基本逻辑放到一个函数中来实现,但是将每次迭代返# 回数值的return换成了yield,此时新定义的函数便不再是函数,而是一个生成器了。简单来说:只要在def中有yield关键字# 的 就称为 生成器
生成器使用return 关键字
def fibonacci(num):
a = 0
b = 1
# 记录生成fibonacci数字的下标
current_index = 0
print("--11---")
while current_index < num:
result = a
a, b = b, a + b
current_index += 1
print("--22---")
# 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
yield result
print("--33---")
return "嘻嘻"
fib = fibonacci(5)
value = next(fib)
print(value)
# 提示: 生成器里面使用return关键字语法上没有问题,但是代码执行到return语句会停止迭代,抛出停止迭代异常
# 在python3里面可以使用return关键字,python2不支持
# return 和 yield的区别
# yield: 每次启动生成器都会返回一个值,多次启动可以返回多个值,也就是yield可以返回多个值
# return: 只能返回一次值,代码执行到return语句就停止迭代
try:
value = next(fib)
print(value)
except StopIteration as e:
# 获取return的返回值
print(e.value)
# 生成器里面使用return关键字语法上没有问题,但是代码执行到return语句会停止迭代,抛出停止迭代异常
# 在python3里面可以使用return关键字,python2不支持
yield和return的对比
使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
每次启动生成器都会返回一个值,多次启动可以返回多个值,也就是yield可以返回多个值
return只能返回一次值,代码执行到return语句就停止迭代,抛出停止迭代异常
简单协程的实现
import time
def work1():
while True:
print("----work1---")
yield
time.sleep(0.5)
def work2():
while True:
print("----work2---")
yield
time.sleep(0.5)
def main():
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
if __name__ == "__main__":
main()
greenlet库的使用
import time
import greenlet
# 任务1
def work1():
for i in range(5):
print("work1...")
time.sleep(0.2)
# 切换到协程2里面执行对应的任务
g2.switch()
# 任务2
def work2():
for i in range(5):
print("work2...")
time.sleep(0.2)
# 切换到第一个协程执行对应的任务
g1.switch()
if __name__ == '__main__':
# 创建协程指定对应的任务
g1 = greenlet.greenlet(work1)
g2 = greenlet.greenlet(work2)
# 切换到第一个协程执行对应的任务
g1.switch()
gevent----强大的异步并发库
greenlet已经实现了协程,但是这个还要人工切换,这里介绍一个比greenlet更强大而且能够自动切换任务的第三方库,那就是gevent。
gevent内部封装的greenlet,其原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。
由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO
import gevent
def work(n):
for i in range(n):
# 获取当前协程
print(gevent.getcurrent(), i)
g1 = gevent.spawn(work, 5)
g2 = gevent.spawn(work, 5)
g3 = gevent.spawn(work, 5)
g1.join()
g2.join()
g3.join()
import gevent
def work(n):
for i in range(n):
# 获取当前协程
print(gevent.getcurrent(), i)
#用来模拟一个耗时操作,注意不是time模块中的sleep
gevent.sleep(1)
g1 = gevent.spawn(work, 5)
g2 = gevent.spawn(work, 5)
g3 = gevent.spawn(work, 5)
g1.join()
g2.join()
g3.join()
import gevent
import time
from gevent import monkey
# 打补丁,让gevent框架识别耗时操作,比如:time.sleep,网络请求延时
monkey.patch_all()
# 任务1
def work1(num):
for i in range(num):
print("work1....")
time.sleep(0.2)
# gevent.sleep(0.2)
# 任务1
def work2(num):
for i in range(num):
print("work2....")
time.sleep(0.2)
# gevent.sleep(0.2)
if __name__ == '__main__':
# 创建协程指定对应的任务
g1 = gevent.spawn(work1, 3)
g2 = gevent.spawn(work2, 3)
# 主线程等待协程执行完成以后程序再退出
g1.join()
g2.join()
进程,线程,协程的区别
进程是资源分配的单位
线程是操作系统调度的单位
进程切换需要的资源最大,效率很低
线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
协程切换任务资源很小,效率高
多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发
进程、线程、协程都是可以完成多任务的,可以根据自己实际开发的需要选择使用
由于线程、协程需要的资源很少,所以使用线程和协程的几率最大
开辟协程需要的资源最少