生成器
知识点:生成器就是通过封装的算法进行边循环边计算的机制,通过next()方法可以每次取一个计算的值,也就是yield b 这个b是多少(x = next(generator) 这个x就是取到的b)。x = yield,那么x 就是用于接收外部发来的值(generator.send(5),那么x就是5)。
通过列表生成式我们可以取得想要的值,但是如果我们创建了一个包含了百万个值的列表,而只要取前面的几个值,显然这造成了内存和时间的浪费。此时,如果我们能用某种方法来把我们想要的值循环计算出来,那么这无疑解决了上面的问题。这种通过某种算法进行边循环边计算的功能,称作生成器。
创建一个生成器的方法有
1.直接把列表推导式的[] 变为 ()
>>> g1 = (x for x in range(5))
>>> next(g1) # 通过 next(g1) 或者 g1.send(None) 或者 g1,__next__() 启动生成器 (next(g1) == g1.send(None))
0
>>> next(g1) # 再通过next(g1) g1.send(None) 就可以取得下一个值或者 g1.send(5)
1
>>> g1.send(4)
2
>>> list(g1) # 一次性取出剩下的值
[3, 4]
>>> next(g1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
当然,重新创建一个生成器g1,我们可以直接用for循环进行迭代取值
>>> g1 = (x for x in range(5))
>>> for i in g1:
... print(i)
...
0
1
2
3
4
2.当需要计算一些复杂的值时(比如计算斐波那契数),用列表推导式无法实现。 这时候我们要用函数实现。
def fib(max):
n, a, b = 0, 0, 1
print('aaa')
while n < max:
print(b)
a, b = b, a + b
n = n + 1
print('a')
>>>fib(6)
aaa
1
1
2
3
5
8
a
我们说过生成器保存的是算法,如果这个算法封装在函数里,那我们如何将这个函数变成生成器呢? 将yield放在循环中就可以实现。yield可以接收外部发送的值(消费者生产者模式就是利用这个特性),还可以用于生成函数内部迭代的值
(1) yield用于生成函数内部迭代的值
def fib(max):
n, a, b = 0, 0, 1
print('aaa')
while n < max:
yield b
a, b = b, a + b
n = n + 1
print('a')
>>> g = fib(5)
>>> for i in g:
... print(i)
...
aaa
1
1
2
3
5
a
函数的生成器也可以用next启动,取值
>>> g = fib(5)
>>> next(g) # 解析:启动生成器,程序执行到yield处等待,接下来yield就可以用于接收值(下面会有生产者消费者模式)。以后每次next都会循环一次while语句
aaa
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> g.send(5)
5
(2)yield可以接收外部发送的值(生产者消费者模式)
def consumer(name):
weikou = 0
while weikou < 10:
egg = yield
print("%s 在吃 egg [%s]" % (name,egg))
weikou += 1
c = consumer('Tom')
next(c) # 此处等待接收
c.send(1) # Tom 在吃 egg [1]
c.send(2) # Tom 在吃 egg [2]
def producer():
c1 = consumer("Tom")
c2 = consumer("John")
c1.__next__()
c2.send(None)
for i in range(3):
c1.send(i)
c2.send(i)
p = producer()
out-------------
Tom 在吃 egg [1]
Tom 在吃 egg [2]
Tom 在吃 egg [0]
John 在吃 egg [0]
Tom 在吃 egg [1]
John 在吃 egg [1]
Tom 在吃 egg [2]
John 在吃 egg [2]
通过队列的生产者消费者模式 https://blog.csdn.net/qq_34964399/article/details/78718279
上面这段代码是最简单的单线程下的并行的例子,也即协程,能同时进行多个任务。例子中就是2个消费者在同时吃包子,如果不用生成器的话,那么就需要等到一个人吃完了包子,另一个人再吃。
迭代器
用于for循环的数据类型有2类。
1.集合数据类型 list,dict,tuple,set,str…
2.generator。包括生成器和带yield的generator function
只要是可以用for循环,那他就是可迭代对象(Iterable)。
>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance([1,2,3],Iterable)
True
>>> isinstance((),Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True
而像生成器这种还能用next()不断调用取值的就是迭代器。而集合数据类型的就不是迭代器
>>> from collections import Iterator
>>> isinstance([],Iterator)
False
>>> isinstance((x for x in range(10)),Iterator)
True
如果想把集合数据类型转为迭代器,可以用iter方法进行转换
>>> isinstance(iter([]),Iterator)
True
其实python的for循环本质上也是用next()调用的。
for i in [1,2,3,4,5]:
print(i)
#等价于
it = iter([1,2,3,4,5])
while True:
try:
x = next(it)
print(x)
except StopIteration as e:
break
协程定义
1.必须在只有一个单线程里实现并发
2.修改共享数据不需加锁
3.用户程序里自己保存多个控制流的上下文栈
4.一个协程遇到IO操作自动切换到其它协程
根据协程定义,上面的写的生产者消费者模型并不属于严格意义上的协程,因为并没有进行IO操作
协程读取数据
我们在定义函数的时候在前面加上async修饰,在耗时任务那行代码使用await修饰,这时候调用函数,它就会返回一个协程(coroutine)对象,然后调用asyncio.run()把协程对象丢进去就能执行了
def get_tables_lst(schema):
if schema == 'gp20i17mp_lf':
tables = 'l9_drt_paa_entity,l9_drt_paa_run_detail,l9_drt_paa_insurance_contract_group,l9_drt_paa_insurance_contract_group_detl,l9_drt_paa_insurance_contract_group_assoc,l9_drt_paa_insurance_contract_portfolio,l9_drt_paa_calc_input_measure'
elif schema == 'gp20i17mp_hf':
tables = 'l9_drt_paa_entity,l9_drt_paa_run_detail,l9_drt_paa_insurance_contract_group,l9_drt_paa_insurance_contract_group_detl,l9_drt_paa_insurance_contract_group_assoc,l9_drt_paa_insurance_contract_portfolio,l9_drt_paa_calc_input_measure'
return tables.split(",")
tables_lst = get_tables_lst(schema)
class DBPool(object):
def __init__(self):
# self.coon = None
self.pool = None
# self.lg = lg
async def initpool(self):
try:
# self.lg.logger.debug("即将连接postgresql")
print("即将连接postgresql")
self.pool = await asyncpg.create_pool(
min_size=5,
max_size=10,
user='read',
host='*****',
port=3432,
password="****",
database="ifrs17db")
except Exception:
pass
async def quety_to_pd(self, query, table_name):
conn = await self.pool.acquire()
try:
result = await conn.fetch(query)
print("table_name: " , table_name, " columns: ", len([x for x in result[0].keys()]) ," rows: ", len(result) + 1)
except Exception as e:
print("error", e)
finally:
# 释放掉conn,将连接放回到连接池中
await self.pool.release(conn)
db = DBPool()
loop = asyncio.get_event_loop()
tasks = [db.initpool()]
loop.run_until_complete(asyncio.wait(tasks))
tasks=[]
for table in tables_lst: #
sql = "SELECT * FROM {}.{}".format(schema, table)
tasks.append(db.quety_to_pd(sql,table))
loop.run_until_complete(asyncio.wait(tasks))
![在这里插入图片描述](https://img-blog.csdnimg.cn/bdba2c0386794ebe94cb653042c05291.png)
import time,asyncio
# await: 当该任务被挂起后,CPU会⾃动切换到其他任务中
async def func1():
print("func1, start")
await asyncio.sleep(3)
print("func1, end")
async def func2():
print("func2, start")
await asyncio.sleep(4)
print("func2, end")
async def func3():
print("func3, start")
await asyncio.sleep(2)
print("func3, end")
# if __name__ == '__main__':
# start = time.time()
# tasks = [ # 协程任务列表
# func1(), # 创建协程任务
# func2(),
# func3()
# ]
# lop = asyncio.get_event_loop()
# lop.run_until_complete(func1())
# # 我要执⾏这个协程任务列表中的所有任务
# lop.run_until_complete(asyncio.wait(tasks))
# # 我要执⾏这个协程任务列表中的所有任务
# print(time.time() - start)
# lop.close()
'''
func1, start
func1, end --会先等func1执行完
func1, start
func2, start
func3, start
func3, end
func1, end
func2, end
7.0143232345581055
'''
# 上⾯的程序还可以写成这样
async def main():
print("start")
tasks = [
func1(),
func2(),
func3()
]
# ⼀次性把所有任务都执⾏
await asyncio.wait([func1()]) # 也会等这个运行完
done, pedding = await asyncio.wait(tasks)
print("end")
if __name__ == '__main__':
start = time.time()
asyncio.run(main())
print(time.time() - start)
'''
start
func1, start
func1, end
func1, start
func3, start
func2, start
func3, end
func1, end
func2, end
end
7.014348268508911
[Finished in 7.2s]
'''