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. 

 

posted @ 2024-05-21 19:55  AlphaGeek  阅读(35)  评论(0)    收藏  举报