Chapter5_非阻塞数据库驱动程序
1、如何理解如下代码,尤其是协程部分【详细解释 insert_brands 函数】。[example5_5.py]
import asyncpg import asyncio from typing import List, Tuple, Union from random import sample
def load_common_words() -> List[str]: with open('common_words.txt') as common_words: return common_words.readlines() def generate_brand_names(words: List[str]) -> List[Tuple[Union[str, ]]]: return [(words[index],) for index in sample(range(100), 100)] async def insert_brands(common_words, connection) -> int: brands = generate_brand_names(common_words) insert_brands = "INSERT INTO brand VALUES(DEFAULT, $1)" return await connection.executemany(insert_brands, brands) async def main(): common_words = load_common_words() connection = await asyncpg.connect(host='127.0.0.1', port=5432, user='postgres', database='products', password='password') await insert_brands(common_words, connection) asyncio.run(main())
这个函数的作用是将生成的品牌名称列表插入到数据库中的 brand 表中。下面是逐行解释,尤其关注最后两行,特别是 executemany 函数的 API 含义。
函数签名
async def insert_brands(common_words, connection) -> int:
async def: 这是一个异步函数,意味着它可以在执行中等待其他异步操作完成。common_words: 一个单词列表,用于生成品牌名称。connection: 一个异步 PostgreSQL 数据库连接对象。-> int: 指定函数返回一个整数。
生成品牌名称
brands = generate_brand_names(common_words)
- 调用
generate_brand_names函数,将common_words列表传递给它。 generate_brand_names返回一个包含品牌名称的列表brands,每个品牌名称是一个单元素元组。
SQL 插入语句
insert_brands = "INSERT INTO brand VALUES(DEFAULT, $1)"
- 定义 SQL 插入语句
insert_brands。 DEFAULT: 用于自动生成 ID 或其他默认值。$1: 占位符,表示将传递的值插入到该位置。
异步执行插入操作
return await connection.executemany(insert_brands, brands)
await: 等待异步操作完成。在等待期间,事件循环可以执行其他任务。connection.executemany: 异步执行多条 SQL 插入语句。insert_brands: SQL 插入语句。brands: 要插入的品牌名称列表,每个元素都是一个单元素元组,对应 SQL 语句中的$1占位符。
executemany 函数的 API 含义
executemany 是 asyncpg 库中的一个方法,用于执行多条 SQL 语句。它的签名如下:
asyncpg.Connection.executemany(command: str, args: Iterable[Iterable[Any]]) -> str
command: str: 要执行的 SQL 命令字符串,可以包含一个或多个占位符(如$1,$2)。args: Iterable[Iterable[Any]]: 一个可迭代对象,其中每个元素是一个包含参数的可迭代对象,对应 SQL 命令中的占位符。- 返回值:
str,表示已执行的命令(通常是执行的 SQL 语句)。
在你的代码中,executemany 方法执行如下:
insert_brands是 SQL 插入语句"INSERT INTO brand VALUES(DEFAULT, $1)"。brands是包含品牌名称的列表,每个品牌名称是一个单元素元组[(brand1,), (brand2,), ..., (brand100,)]。
executemany 方法会将 brands 中的每个元组的值插入到 insert_brands 语句中的 $1 位置,并异步地将这些语句发送到数据库执行。这种方法可以高效地批量插入数据,而不是一次一条地插入。
完整的执行过程
-
生成品牌名称:
brands = generate_brand_names(common_words)
-
生成一个包含品牌名称的列表
brands。 -
定义 SQL 插入语句:
insert_brands = "INSERT INTO brand VALUES(DEFAULT, $1)"
-
SQL 插入语句,使用
$1作为占位符。 -
异步批量插入:
return await connection.executemany(insert_brands, brands)
-
异步执行
executemany方法,将brands中的每个品牌名称插入到brand表中。
这种设计允许在插入大量数据时更高效,因为它减少了数据库交互的次数,通过批量插入提高了性能。
2、如何理解SQL的注入攻击?
3、理解如下代码,以及不能并发执行的原因。然后优化它,使其能够并行。[from example5_14.py]
import asyncio from util import delay, async_timed async def positive_integers_async(until: int): for integer in range(1, until): """That's the key reason which example5_14.py running as serial execution, instead of concurrent execution""" await delay(integer) yield integer @async_timed() async def main(): async_generator = positive_integers_async(3) print(type(async_generator)) async for number in async_generator: print(f'Got number {number}') asyncio.run(main())
优化后:
import asyncio from util import delay, async_timed async def positive_integers_async(until: int): tasks = [] for integer in range(1, until): task = asyncio.create_task(delay_and_yield(integer)) tasks.append(task) results = await asyncio.gather(*tasks) # result: list print(type(results)) for result in results: # can't use async for, because result is a list, not async_generator. yield result # yield -> generator async def delay_and_yield(integer): await delay(integer) return integer @async_timed() async def main(): async_generator = positive_integers_async(4) print(type(async_generator)) async for number in async_generator: # async can use here, because async_generator is <class 'async_generator'> print(f'Got number {number}') asyncio.run(main())
2

浙公网安备 33010602011771号