扩展:enumerate方法
names = [1, 2, 3]
for temp in names:
print(temp)
# 通过enumerate可以得到列表的索引
for temp in enumerate(names):
print(temp)
# 对返回的结果直接进行拆包
for i, temp in enumerate(names):
print(i, temp)
实现多任务的三种方式:
- 线程(根据cpu核数,可能是并行)
- 进程(根据cpu核数,可能是并行)
- 协程(并发)
线程
子线程创建的时间
import threading
import time
def test():
for i in range(5):
print("-------test1------%d" % i)
def main():
# Thread只是创建了一个对象
t1 = threading.Thread(target = test)
# 当调用start时,才会创建一个子线程并执行
t1.start()
if __name__ == "__main__":
main()
知识点:
- 主线程结束,子线程会立即结束
- 多个子线程之间执行,没有先后顺序;如果想要控制顺序,可以通过time.sleep()来延时执行
- 子线程是在start方法调用时,才会创建并执行的
- 线程必须存在于进程中
创建对象有两种方法:
- 直接用
threading.Thread(target = 函数名)
- 重写写一个新的类,继承threading.Thread,并且在新的类中,必须写run方法(这种方法适用于这个线程中,需要处理多个函数时)
import threading
import time
class NewThread(threading.Thread):
# 除了run方法,还可以写别的方法
# 这种方法适用于这个线程中,需要处理多个方法时
# 在调用start()时,系统会自动调run(),所以这个类中的别的方法,需要在run()中调用。
def run(self):
for i in range(5):
time.sleep(1)
msg = "I'm " + self.name + " @ " + str(i)
print(msg)
if __name__ == "__main__":
t = NewThread()
t.start()
线程之间数据共享,使用全局变量
import threading
import time
num = 100
def test_o():
global num
num += 100
print("--------test_o--------%d" % num)
def test_t():
print("--------test_t--------%d" % num)
def main():
t1 = threading.Thread(target = test_o)
t2 = threading.Thread(target = test_t)
t1.start()
# 通过延时,保证函数执行的先后顺序
time.sleep(1)
t2.start()
print("in main %d" % num)
if __name__ == "__main__":
main()
threading.Thread(target= 函数名, args = 参数列表)这个args是一个元组
import threading
import time
g_nums = [11, 22, 33]
def test_o(temp):
temp.append(44)
print("------test_o------%s" % str(temp))
def test_t(temp):
print("------test_t------%s" % str(temp))
def main():
t1 = threading.Thread(target = test_o, args = (g_nums, ))
t2 = threading.Thread(target = test_t, args = (g_nums, ))
t1.start()
time.sleep(1)
t2.start()
time.sleep(1)
print("in main temp=%s" % str(g_nums))
if __name__ == "__main__":
main()
但是,在线程同时操作全局变量时,有可能会出现资源竞争,计算错误
互斥锁
import threading
import time
g_num = 0
# 创建一个互斥锁,解决资源竞争的问题
mutex = threading.Lock()
def test_o(num):
global g_num
for i in range(num):
# 如果已经上锁,默认会堵塞,直到解锁
mutex.acquire()
g_num += 1
mutex.release()
print("g_num = %d" % g_num)
def test_t(num):
global g_num
for i in range(num):
mutex.acquire()
g_num += 1
mutex.release()
print("g_num = %d" % g_num)
def main():
t1 = threading.Thread(target = test_o, args = (100000, ))
t2 = threading.Thread(target = test_t, args = (100000, ))
t1.start()
t2.start()
time.sleep(5)
print("g_num = %d" % g_num)
if __name__ == "__main__":
main()
进程
使用进程实现多任务
import time
import multiprocessing
def test_o():
for i in range(5):
print("----test_o-----%d" % i)
time.sleep(1)
def test_t():
for i in range(5):
print("------test_t-------%d" % i)
time.sleep(1)
def main():
p1 = multiprocessing.Process(target=test_o)
p2 = multiprocessing.Process(target=test_t)
p1.start()
p2.start()
if __name__ == "__main__":
main()
多进程之间通过Queue队列来进行通信
import multiprocessing
def down_web(q):
data = [11, 22, 33, 44]
for temp in data:
# 放入数据
q.put(temp)
print("数据已全部存入")
def get_data(q):
writting = list()
while True:
# 取出数据
data = q.get()
writting.append(data)
# 判断队列是否为空,通过q.full()可以判断队列是否已满
if q.empty():
break
print(writting)
def main():
# 1.创建一个队列
q = multiprocessing.Queue()
# 2.创建多个进程,将多个队列当作实参传递到里面
p1 = multiprocessing.Process(target = down_web, args = (q, ))
p2 = multiprocessing.Process(target = get_data, args = (q, ))
p1.start()
p2.start()
if __name__ == "__main__":
main()
进程池
如果任务量大时,不可能一次性创建非常多的进程,所以创建一个进程池,不停的向进程池中添加任务,执行完的进程会继续执行之后的任务,如果都在执行,则之后的任务就会堵塞
注意:使用进程池创建Queue队列时,需要用Manger对象的Queue方法
from multiprocessing import Pool
import os
import time
import random
def test(num):
# 获取当前时间
t_start = time.time()
# random.random()随机生成一个0-1之间的浮点数
time.sleep(random.random()*2)
t_stop = time.time()
# 通过os.getpid(),来得到进程id
print("start = %s,pid = %d" % (num, os.getpid()))
print("time = %0.2f" % (t_stop - t_start))
def main():
po = Pool(3)
for i in range(0, 10):
po.apply_async(test, (i, ))
# 关闭进程池,不允许新的任务进入
po.close()
# 等待所有任务执行完毕
po.join()
if __name__ == "__main__":
main()
协程
迭代器
可以节省内存空间,实现循环
迭代器执行原理:
- 如果想要一个对象成为可以迭代的对象,类中必须实现__iter__这个方法
- 这个方法需要返回一个同时包含__iter__和__next__这两个方法的类(也可以是自己)
- 在执行for来进行遍历迭代时,系统会自动执行__iter__这个方法生成一个迭代器,每循环一次,迭代器会自动调用__next__方法一次
- 我们需要将第一个类中需要遍历的变量传入__iter__调用的类中,让第二个类中的__next__方法来调用,不断输出遍历的值
- 如果遍历结束,需抛出一个异常,raise StopIteration,for循环得到这个异常,会自动结束
我们使用list()将元组转换成元组,用tuple()将列表转换成元组都用到了迭代器,不断将数据取出,重新建立新的列表进行append()
from collections.abc import Iterable
from collections.abc import Iterator
class ClassMate(object):
"""docstring for ClassMate"""
def __init__(self):
self.names = list()
self.count = 0
def add(self, name):
self.names.append(name)
def __iter__(self):
"""如果想要一个对象成为可以迭代的对象,则类中需要实现__iter__这个方法"""
# 需要返回一个同时拥有iter()和next()这两个方法的类
return self
def __next__(self):
if self.count < len(self.names):
ret = self.names[self.count]
self.count += 1
return ret
else:
raise StopIteration
classmate = ClassMate()
classmate.add("test1")
classmate.add("test2")
classmate.add("test3")
# isinstance(对象, Iterable) 验证一个对象是否可以迭代
print("classmate 是否可以迭代", isinstance(classmate, Iterable))
classmate_iterator = iter(classmate)
# isinstance(迭代器, Iterator),验证一个返回值是否是迭代器
print("classmate_iterator 是否是一个迭代器", isinstance(classmate_iterator, Iterator))
for temp in classmate:
print(temp)
生成器
生成器是特殊的迭代器,可以让函数在执行一部分时暂停,随后还可以继续执行
生成器的原理:
- 在方法中写入yield时,这个就不再是一个函数了,而是一个生成器模板
- 通过接收返回值得到生成器对象
- 不停的执行next(生成器对象),可以得到迭代器相同的效果(在执行next()时,yield会将后面的数据直接传入接收的变量中,但是程序不会停止,只是暂停在当前位置,当你继续执行next()时,程序会继续执行),事实上,调用for进行迭代,系统就会自动调用next()
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
# 如果一个函数中有yield函数,那么这个函数就不再是函数了,而是一个生成器模板
yield a
a, b = b, a+b
current_num += 1
return "ok....."
# 如果在调用create_num的时候,发现这个函数有yield,那么此时不是调用函数,而是生成生成器对象
obj = create_num(10)
for temp in obj:
print(temp)
# 或者使用while循环,当出现StopIteration时,就会停止
while True:
try:
ret = next(obj)
print(ret)
except Exception as ret:
print(ret.value)
break
def create_num(all_num):
a, b = 0, 1
current_num = 0
while current_num < all_num:
# 如果一个函数中有yield函数,那么这个函数就不再是函数了,而是一个生成器模板
# ret可以接受send()传过来的值
ret = yield a
print("ret =========>", ret)
a, b = b, a+b
current_num += 1
# 如果在调用create_num的时候,发现这个函数有yield,那么此时不是调用函数,而是生成生成器对象
obj = create_num(10)
# 连续执行next()也可以得到结果
ret = next(obj)
ret = next(obj)
ret = next(obj)
# send()和next()大体作用一样,只不过send()可以进行传值
# send()一般不在第一次使用,会报错,如果非得使用,send(None)
ret = obj.send("hahaha")
print(ret)
通过yield实现多任务
通过利用yield的特性来实现多任务,因为yield拥有可以暂停后继续执行,并且数据不会丢的特性
import time
def task_o():
while True:
print("------task_o-------")
time.sleep(0.1)
yield
def task_t():
while True:
print("-----task_t--------")
time.sleep(0.1)
yield
def main():
# 创建两个生成器
t1 = task_o()
t2 = task_t()
while True:
# 交替执行,当执行完next(t1)后遇到yield时,就会退出,然后开始执行next(t2),执行完后就会退出,继续执行next(t1),数据也不会清除,实现了多任务
next(t1)
next(t2)
if __name__ == "__main__":
main()
通过协程来实现多任务
通过时间阻塞时,自动切换任务来实现多任务
3 import gevent
4 import time
5
6
7 def task_o(n):
8 for i in range(n):
9 print(gevent.getcurrent(), i)
10 # time.sleep(0.1)
11 gevent.sleep(0.5)
12
13 def task_t(n):
14 for i in range(n):
15 print(gevent.getcurrent(), i)
16 # time.sleep(0.1)
17 gevent.sleep(0.5)
18
19 def task_h(n):
20 for i in range(n):
21 print(gevent.getcurrent(), i)
22 # time.sleep(0.1)
23 gevent.sleep(0.5)
24
25
26 print("-------1-------")
27 g1 = gevent.spawn(task_o, 10)
28 print("-------2-------")
29 g2 = gevent.spawn(task_t, 10)
30 print("-------3-------")
31 g3 = gevent.spawn(task_h, 10)
32 print("-------3-------")
33 g1.join()
34 g2.join()
35 g3.join()
如果我们的程序之前已经存在许多耗时操作,那么我们可以通过monkey.path_all()来改变所有的耗时操作为gevent()
3 import gevent
4 import time
5 from gevent import monkey
6
7
8 # 当程序存在耗时操作时,他会自动将耗时代码改为gevent,实现多任务
9 monkey.patch_all()
10
11
12 def task_o(n):
13 for i in range(n):
14 print(gevent.getcurrent(), i)
15 time.sleep(0.5)
16
17
18 def task_t(n):
19 for i in range(n):
20 print(gevent.getcurrent(), i)
21 time.sleep(0.5)
22
23
24 def task_h(n):
25 for i in range(n):
26 print(gevent.getcurrent(), i)
27 time.sleep(0.5)
28
29
30 g1 = gevent.spawn(task_o, 10)
31 g2 = gevent.spawn(task_t, 10)
32 g3 = gevent.spawn(task_h, 10)
33 g1.join()
34 g2.join()
35 g3.join()
也可以这样
3 import gevent
4 import time
5 from gevent import monkey
6
7 # 当程序存在耗时操作时,会自动替换为gevent
8 monkey.patch_all()
9
10
11 def task_o(n):
12 for i in range(n):
13 print(gevent.getcurrent(), i)
14 time.sleep(0.5)
15
16
17 def task_t(n):
18 for i in range(n):
19 print(gevent.getcurrent(), i)
20 time.sleep(0.5)
21
22
23 def task_h(n):
24 for i in range(n):
25 print(gevent.getcurrent(), i)
26 time.sleep(0.5)
27
28 # 将所有要进行操作的方法,添加到列表中,同上面的效果一样,省时省力
29 gevent.joinall([
30 gevent.spawn(task_o, 10),
31 gevent.spawn(task_t, 10),
32 gevent.spawn(task_h, 10)
33 ])