可迭代对象及检测方法
- 可迭代对象
- 可遍历对象就是可迭代对象
- 列表、元组、字典、字符串都是可迭代对象
- int数字 和 自定义myclass 默认都是不可以迭代的
- myclass2 对象所属的类 MyClass2 如果包含了 iter() 方法,此时myclass2 就是一个可迭代对象
- 可迭代对象的本质:对象所属的类中包含了 iter() 方法
- 可迭代对象的检测:检测一个对象是否可以迭代,用 isinstance() 函数检测
"""
isinstance(待检测的对象, Iterable)
返回值为:True 可以迭代
False 不可能迭代
"""
# from collections import Iterable 3.10以上版本取消了以下方法
# ["Awaitable", "Coroutine", "AsyncIterable", "AsyncIterator", "AsyncGenerator", "Hashable", "Iterable", "Iterator", "Generator", "Reversible", "Sized", "Container", "Callable", "Collection", "Set", "MutableSet", "Mapping", "MutableMapping", "MappingView", "KeysView", "ItemsView", "ValuesView", "Sequence", "MutableSequence", "ByteString"]
from collections.abc import Iterable
print(isinstance([1, 2, 3], Iterable))
print(isinstance((1, 2, 3), Iterable))
print(isinstance({"a": 1, "b": 2, "c": 3}, Iterable))
print(isinstance("abcdefg", Iterable))
print(isinstance(100, Iterable)) # False
# 类
class MyClass():
pass
myclass = MyClass()
print(isinstance(myclass, Iterable)) # False
class MyClass2():
# 增加一个__iter__方法,该方法就是一个迭代器
def __iter__(self):
pass
myclass2 = MyClass2()
print(isinstance(myclass2, Iterable)) # True
True
True
True
True
False
False
True
迭代器及其使用方法
- 迭代器的作用:
- 记录当前迭代的位置
- 配合next() 获取可迭代对象的下一个元素值
- 获取迭代器:iter(可迭代对象)
- 获取可迭代对象的值:next(迭代器)
- for循环的本质:
- 通过 iter(要遍历的对象) 获取要遍历的对象的迭代器
- next(迭代器)获取下一个元素
- 帮我们捕获了 StopIteration 异常
- 自定义迭代器类
"""
1) 一个可迭代对象可以提供一个迭代器
2)可迭代对象--->iter(可迭代对象) ---> next(迭代器)
迭代器 下一个元素
迭代器特点:
1)记录遍历的位置
2)提供下一个元素的值(配合next()函数)
for循环的本质:
1)通过 iter(要遍历的对象) 获取要遍历的对象的迭代器
2)next(迭代器)获取下一个元素
3)帮我们捕获了 StopIteration 异常
"""
# 1、data_list1 是一个可迭代对象
data_list1 = [1, 3, 5, 7, 9]
# for value in data_list1:
# print(value)
# 2、获取迭代器
l1_iterator = iter(data_list1)
# 3、根据迭代器,可以获取下一个元素
value = next(l1_iterator)
print(value) # 1
value = next(l1_iterator)
print(value) # 3
value = next(l1_iterator)
print(value) # 5
value = next(l1_iterator)
print(value) # 7
value = next(l1_iterator)
print(value) # 9
value = next(l1_iterator)
print(value) # 报错 StopIteration
# 自定义迭代器类,满足2点
# 1)必须含有 __iter__()
# 2) 必须含有 __next__()
class MyIterator(object):
def __iter__(self):
pass
# 当 next(迭代器) 的时候,会自动调用该方法
def __next__(self):
pass
迭代器应用:自定义列表
# 创建MyList类
class MyList(object):
# 初始化方法
def __init__(self):
# 定义实例属性,保存数据
self.items = []
# __iter__()方法,对外提供迭代器
def __iter__(self):
# 创建MyListIterator对象
mylist_iterator = MyListIterator(self.items)
return mylist_iterator
# addItem()方法,用来添加数据
def addItem(self, data):
self.items.append(data)
# print(self.items)
# 自定义迭代器类
class MyListIterator(object):
# 初始化方法
def __init__(self, items):
# 定义实例属性,保存传递过来的items
self.items = items
# 记录迭代器迭代的位置
self.current_index = 0
# 迭代器方法
def __iter__(self):
pass
# 获取下一个元素值的方法
def __next__(self):
# 判断当前的下标是否越界
if self.current_index < len(self.items):
# 根据下标获取下标对应的元素值
data = self.items[self.current_index]
# 下标位置+1
self.current_index += 1
# 返回下标对应的数据
return data
# 如果越界,直接抛出异常
else:
# raise 主动抛出异常 StopIteration 停止迭代
raise StopIteration
if __name__ == '__main__':
mylist = MyList()
mylist.addItem("a")
mylist.addItem("b")
mylist.addItem("c")
for value in mylist:
print(value)
my_iterator = iter(mylist)
value = next(my_iterator)
print(value)
value = next(my_iterator)
print(value)
value = next(my_iterator)
print(value)
value = next(my_iterator) # StopIteration
print(value)
# a
# b
# c
# a
# b
# c
迭代器:斐波那契数列
"""
自定义迭代器:
1) 定义迭代器类
2) 类中必须 __iter__() 方法
3) 类中必须 __next__() 方法
目标:
# 指定生成5列的斐波那契数列
fib = Fibnacci(5)
value = next(fib)
print(value)
"""
class Fibnacci(object):
def __init__(self, num):
# 定义实例属性,保存生成的列数
self.num = num
# 定义变量保存斐波那契数列的第一列和第二列
self.a = 1
self.b = 1
# 记录下标位置的实例属性
self.current_index = 0
# __iter__()
def __iter__(self):
# 返回自己
return self
# __next__(self)
def __next__(self):
# 判断列数是否超过生成的总列数
if self.current_index < self.num:
# 定义变量,保存a的值 a=b b=a+b 当前列数+1 返回a的值
data = self.a
self.a, self.b = self.b, self.a + self.b
self.current_index += 1
return data
# 如果超出范围报错
else:
# 主动抛出异常
raise StopIteration
if __name__ == '__main__':
for value in Fibnacci(5):
print(value)
# 创建迭代器对象
fib_iterator = Fibnacci(5)
value = next(fib_iterator)
print(value)
value = next(fib_iterator)
print(value)
value = next(fib_iterator)
print(value)
1
1
2
3
5
生成器
生成器是一种特殊的迭代器,按照一定的规律生成数列
"""
生成器是一种特殊的迭代器(保存位置,返回下一个值)
next(迭代器) 得到下一个值
next(生成器) 也能够得到下一个值
生成器创建方式:
1、列表推导式
2、函数中使用了 yield
"""
# 列表推导式
data_list = [x*2 for x in range(10)]
for value in data_list:
print(value, end=" ")
print()
# 生成器的创建一:
data_list2 = (x*2 for x in range(10)) # 列表推导式的[]换成()
print(data_list2)
# 通过next获取下一个值
value = next(data_list2)
print(value)
value = next(data_list2)
print(value)
print("-----"*10)
def test():
return 10
m = test()
print("m = ", m)
# 使用yield创建了一个生成器对象
def test1():
yield 10
n = test1()
print("n = ", n)
value = next(n)
print(value)
0 2 4 6 8 10 12 14 16 18
<genexpr>
at 0x00000185C055ECF0>
0
2 |
---|
m = 10 |
n = <generator object test1 at 0x00000185BFC90EB0> |
10 |
生成器-案例-斐波那契数列
"""
1、创建一个生成器
目标:实现斐波那契数列
1) 定义变量保存第一列和第二列的值
2) 定义变量保存当前生成的位置
3) 循环生成数据,条件(当前的列数 < 总列数)
4) 保存第一列的值 a
5) 修改 a b 的值(a=b, b = a+b)
6) 返回 a 的值 yield
2、定义变量保存生成器
next(生成器) 得到下一个元素值
"""
def fibnacci(n):
a = 1
b = 1
current_index = 0
print("-----探索yield运行机制11111----")
while current_index < n:
data = a
a, b = b, a + b
current_index += 1
# 充当return作用
# 保存程序的运行状态,并且暂停程序执行
# 当next的时候,可以唤醒程序从yeild位置继续向上执行
print("-----探索yield运行机制22222----")
yield data
print("-----探索yield运行机制33333----")
if __name__ == '__main__':
fib = fibnacci(5)
value = next(fib)
print("---------->", value)
value = next(fib)
print("---------->", value)
value = next(fib)
print("---------->", value)
value = next(fib)
print("---------->", value)
value = next(fib)
print("---------->", value)
-----探索yield运行机制11111----
-----探索yield运行机制22222----
----------> 1
-----探索yield运行机制33333----
-----探索yield运行机制22222----
----------> 1
-----探索yield运行机制33333----
-----探索yield运行机制22222----
----------> 2
-----探索yield运行机制33333----
-----探索yield运行机制22222----
----------> 3
-----探索yield运行机制33333----
-----探索yield运行机制22222----
----------> 5
生成器-使用注意
- return 的作用:可以结束 生成器 的运行
- send的作用,能唤醒生成器,也能传递参数
- next(fib) 可以唤醒 生成器,但是不能传递参数
生成器.send(传递给生成器的值)
- fib.send(1)
- xxx = yield data # xxx = 1
- 使⽤send启动⽣成器的时候传⼊的参数必须是None,下次启动⽣成器的时候可以加上参数
def fibnacci(n):
a = 1
b = 1
current_index = 0
print("-----探索yield运行机制11111----")
while current_index < n:
data = a
a, b = b, a + b
current_index += 1
# 充当return作用
# 保存程序的运行状态,并且暂停程序执行
# 当next的时候,可以唤醒程序从yeild位置继续向上执行
print("-----探索yield运行机制22222----")
xxx = yield data
print("-----探索yield运行机制33333----")
# 生成器中能使用return,让生成器结束
# return "return 能让生成器结束!"
if xxx == 1:
return
if __name__ == '__main__':
fib = fibnacci(5)
value = next(fib)
print("---------->", value)
try:
value = next(fib)
print("---------->", value)
value = fib.send(1)
print("---------->", value)
value = next(fib)
print("---------->", value)
except Exception as e:
print(e)
-----探索yield运行机制11111----
-----探索yield运行机制22222----
----------> 1
-----探索yield运行机制33333----
-----探索yield运行机制22222----
----------> 1
-----探索yield运行机制33333----
协程-yield
又称微线程,纤程,从技术的角度来说,“协程就是你可以暂停执行的函数”。
协程的意义:协程只使用一个线程(单线程),在一个线程中规定某个代码块执行顺序
协程的适用场景:当程序中存在大量不需要CPU的操作时(使用IO),适用于协程
# 简单的协程
import time
# 创建work1的生成器
def work1():
while True:
print("正在执行work1。。。")
yield
time.sleep(0.5)
# 创建work2的生成器
def work2():
while True:
print("正在执行work2。。。")
yield
time.sleep(0.5)
if __name__ == '__main__':
w1 = work1()
w2 = work2()
while True:
next(w1)
next(w2)
协程-greenlet
- greenlet 实现协程:greenlet 是一个第三方的模块,自行的调度的微线程
- 使用步骤:
- 导入模块:from greenlet import greenlet
- 创建 greenlet 对象:g1 = greenlet(函数名)
- 切换任务:g1. switch()
from greenlet import greenlet
import time
def work1():
while True:
print("正在执行work1。。。")
time.sleep(0.5)
g2.switch()
def work2():
while True:
print("正在执行work2。。。")
time.sleep(0.5)
g1.switch()
if __name__ == '__main__':
g1 = greenlet(work1)
g2 = greenlet(work2)
# 切换到g1任务
g1.switch()
协程-gevent
- gevent 也是第三方库,自动调度协程,自动识别程序中的耗时操作
- 使用步骤:
- 导入模块:import gevent
- 指派任务:g1 = gevent.spawn(函数名, 参数1,参数2,....)
- join() 让主线程等待协程执行完毕后再退出:g1.join()
- gevent 不能识别耗时操作的问题
- 替换time.sleep() ---> gevent.sleep()
- 打猴子补丁
- 导入模块 from gevent import monkey
- 破解所有:monkey.patch_all()
猴子补丁的作用:
- 在运行时替换方法、属性等
- 在不修改第三方代码的情况下增加原来不支持的功能
- 在运行时为内存中的对象增加patch而不是在磁盘的源代码中增加
import gevent
from gevent import monkey
def work1():
while True:
print("正在执行work1。。。")
gevent.sleep(0.5)
def work2():
while True:
print("正在执行work2。。。")
gevent.sleep(0.5)
if __name__ == '__main__':
g1 = gevent.spawn(work1)
g2 = gevent.spawn(work2)
g1.join()
g2.join()
import time
import gevent
from gevent import monkey
monkey.patch_all()
def work1():
while True:
print("正在执行work1。。。")
time.sleep(0.5)
def work2():
while True:
print("正在执行work2。。。")
time.sleep(0.5)
if __name__ == '__main__':
g1 = gevent.spawn(work1)
g2 = gevent.spawn(work2)
g1.join()
g2.join()
进程、线程、协程区别
- 进程资源分配的基本单位、线程CPU调度的基本单位、协程单线程执行多任务
- 切换效率: 协程 > 线程 > 进程
- 高效率方式: 进程 + 协程
-
选择问题:
- 多进程:
- 密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。
- 缺陷:多个进程之间通信成本高,切换开销大。
- 多线程:
- 密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。
- 缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。
- 协程:
- 当程序中存在大量不需要CPU的操作时(IO),适用于协程;
- 缺陷:单线程执行,处理密集CPU和本地磁盘IO的时候,性能较低,处理网络IO性能比较高。
案例-并发下载器
# 导入模块 urllib.request.urlopen() 打开网址并反回对应的内容(二进制流)
import urllib.request
import gevent
def download_img(img_url, file_name):
try:
# 打开网址
reponse_data = urllib.request.urlopen(img_url)
# 在本地创建文件,准备保存
with open(f"./{file_name}", "wb") as file:
while True:
# 读取网络资源数据
file_data = reponse_data.read(1024)
# 判断读取的数据不为空
if file_data:
# 把读取的内容写入到本地文件中
file.write(file_data)
else:
break
except Exception as e:
print(f"文件{file_name}下载失败!{e}")
else:
print(f"文件{file_name}下载成功!")
def main():
# 定义图片的下载路径
img_url1 = "https://v.vimll.com:9999/wp-content/uploads/2020/03/IMG_0192-1620x1080.jpg"
img_url2 = "https://v.vimll.com:9999/wp-content/uploads/2020/03/IMG_0196-1620x1080.jpg"
img_url3 = "https://v.vimll.com:9999/wp-content/uploads/2020/03/IMG_0203-1620x1080.jpg"
# 使用协程调用下载文件的函数 geven.joinall([协程的列表])批量装协程join()
gevent.joinall([
gevent.spawn(download_img, img_url1, "1.jpg"),
gevent.spawn(download_img, img_url2, "2.jpg"),
gevent.spawn(download_img, img_url3, "3.jpg")
])
if __name__ == '__main__':
main()
文件1.jpg下载成功!
文件2.jpg下载成功!
文件3.jpg下载成功!