Chapter2_asyncio基础
1. 为什么如下代码中,最开始两行输出结果,先输出Task not finished, checking again in a second.
再输出sleeping for 10 second(s)?
import asyncio from asyncio import CancelledError import time async def delay(delay_seconds: int) -> int: print(f'sleeping for {delay_seconds} second(s)') await asyncio.sleep(delay_seconds) print(f'finished sleeping for {delay_seconds} second(s)') return delay_seconds async def main(): t0 = time.time() long_task = asyncio.create_task(delay(10)) seconds_elapsed = 0 while not long_task.done(): print('Task not finished, checking again in a second.') await asyncio.sleep(1) seconds_elapsed += 1 if seconds_elapsed == 5: long_task.cancel() try: await long_task except CancelledError as e: print('Our task was cancelled') print(time.time() - t0) asyncio.run(main())
输出结果:
Task not finished, checking again in a second. ? 顺序不应该颠倒吗? sleeping for 10 second(s) ? Task not finished, checking again in a second. Task not finished, checking again in a second. Task not finished, checking again in a second. Task not finished, checking again in a second. Task not finished, checking again in a second. Our task was cancelled 6.066833734512329
在 main 函数中,首先我们创建了一个异步任务 long_task,然后进入了 while 循环。
这个循环的目的是检查 long_task 是否已经完成,如果没有完成则打印一条信息,并等待一秒钟。
在第一次循环之前,任务已经被创建但还没有开始执行。因此,第一个输出语句 "Task not finished, checking again in a second." 先执行。
接着,我们调用 asyncio.sleep(10) 来等待 10 秒钟。这时候 delay 函数开始执行,并且在其中的 await asyncio.sleep(delay_seconds) 这行代码中会等待指定的时间,也就是 10 秒钟。
在这 10 秒钟内,任务正在执行但尚未完成。因此,第二个输出语句 "sleeping for 10 second(s)" 执行。
综上所述,输出的顺序是首先进入循环并打印第一个语句,然后执行异步任务并打印第二个语句。
2. 如下代码的 @functools.wraps(func)的理解。[问题来源于 P41 代码清单2-16]
下面是自己解释和自己举的例。
@functools.wraps(func) 是一个装饰器,用于确保包装后的函数具有与原始函数相同的文档字符串、名称、参数列表和其他属性。
在使用 functools.wraps() 装饰器时,它会将包装函数的一些元信息设置为原始函数的元信息,这样就可以保留原始函数的特性,避免了在包装后丢失有关函数的重要信息。
为了更好地理解 @functools.wraps(func) 的作用,我们可以通过一个例子来说明。假设我们有一个装饰器函数,它用于将函数的调用时间记录下来,并在调用函数后打印出来。
我们希望在装饰器中保留原始函数的文档字符串以及函数名称,以便于调试和查看代码时的可读性。
下面是一个示例:
import functools import time # 装饰器函数,用于记录函数的调用时间 def time_it(func): @functools.wraps(func) def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(f"Function {func.__name__} with docstring {func.__doc__} took {end_time - start_time} seconds to execute.") return result return wrapper # 原始函数 @time_it def example_function(): """This is an example function.""" print("Executing example function.") time.sleep(2) # 调用装饰后的函数 example_function()
返回结果:
Executing example function. Function example_function with docstring This is an example function. took 2.000199317932129 seconds to execute.
在这个示例中,我们定义了一个装饰器函数 time_it,它接受一个函数作为参数,并返回一个新的函数 wrapper。
在 wrapper 函数内部,我们记录了函数的开始时间和结束时间,并计算了函数执行的时间。
然后,我们使用 functools.wraps(func) 装饰器来确保 wrapper 函数具有与原始函数 func 相同的元信息。
通过使用 @functools.wraps(func),我们可以确保 wrapper 函数具有与原始函数相同的文档字符串、名称等属性。
这样,在调用装饰后的函数时,我们可以像调用原始函数一样访问这些属性,提高了代码的可读性和可维护性。
3. 为什么如下装饰器函数非得要实现3个内嵌函数,2个内嵌函数就不行吗?
另外在内嵌函数 wrapped中,return await func(*args, **kwargs) 这里的await是暂停哪个函数:wrapped, wrapper还是async_timed()?
import functools import time from typing import Callable, Any def async_timed(): def wrapper(func: Callable) -> Callable: @functools.wraps(func) async def wrapped(*args, **kwargs) -> Any: print(f'starting {func} with args {args} {kwargs}') start = time.time() try: return await func(*args, **kwargs) finally: end = time.time() total = end - start print(f'finished {func} in {total:.4f} second(s)') return wrapped return wrapper
4.

浙公网安备 33010602011771号