2015-10-29 17 views
15

PEP 0492 thêm phương thức ma thuật __await__ mới. Đối tượng triển khai phương thức này sẽ trở thành đối tượng tương lai giống như và có thể được chờ đợi bằng cách sử dụng await. Rõ ràng:Làm thế nào tôi có thể chờ đợi bên trong của đối tượng tương lai __await__?

import asyncio 


class Waiting: 
    def __await__(self): 
     yield from asyncio.sleep(2) 
     print('ok') 

async def main(): 
    await Waiting() 

if __name__ == "__main__": 
    loop = asyncio.get_event_loop() 
    loop.run_until_complete(main()) 

Ok, nhưng những gì nếu tôi muốn gọi một số async def được xác định chức năng thay vì asyncio.sleep? Tôi không thể sử dụng await__await__ không phải là async chức năng, tôi không thể sử dụng yield from vì coroutines bản địa đòi hỏi await biểu:

async def new_sleep(): 
    await asyncio.sleep(2) 

class Waiting: 
    def __await__(self): 
     yield from new_sleep() # this is TypeError 
     await new_sleep() # this is SyntaxError 
     print('ok') 

Làm thế nào tôi có thể giải quyết nó?

+0

Có lý do gì khiến bạn không thể thực hiện nó như một hàm không đồng bộ riêng biệt bên trong lớp Chờ đợi không? Vì vậy, chờ đợi Waiting.new_sleep()? – shongololo

Trả lời

18

Sử dụng trực tiếp __await__() gọi:

async def new_sleep(): 
    await asyncio.sleep(2) 

class Waiting: 
    def __await__(self): 
     return new_sleep().__await__() 

Các giải pháp đã được đề nghị bởi Yury Selivanov (tác giả của PEP 492) cho aioodbc library

+0

Cảm ơn. Typo trong câu trả lời, đúng - new_sleep() .__ đang chờ __() –

+0

Đã sửa lỗi. Cảm ơn bạn đã chỉnh sửa –

+0

câu trả lời này: https://stackoverflow.com/a/46722215/371191 là tổng quát hơn một chút vì nó cho phép chờ đợi nhiều sự chờ đợi –

3

Tôi không hiểu tại sao tôi không thể năng suất từ ​​coroutine mẹ đẻ bên __await__, nhưng có vẻ như nó có thể để năng suất từ ​​máy phát điện coroutine bên __await__năng suất từ ​​coroutine mẹ đẻ bên trong đó phát coroutine . Nó hoạt động:

async def new_sleep(): 
    await asyncio.sleep(2) 

class Waiting: 
    def __await__(self): 
     @asyncio.coroutine 
     def wrapper(coro): 
      return (yield from coro) 
     return (yield from wrapper(new_sleep())) 
3

Để chờ đợi bên trong một hàm __await__, sử dụng mã sau:

async def new_sleep(): 
    await asyncio.sleep(1) 


class Waiting: 
    def __await__(self): 
     yield from new_sleep().__await__() 
     print('first sleep') 
     yield from new_sleep().__await__() 
     print('second sleep') 
     return 'done' 
2

phiên bản ngắn: await foo có thể được thay thế bằng yield from foo.__await__()


Kết hợp tất cả các ý tưởng từ những câu trả lời khác -

trong trường hợp đơn giản nhất, chỉ cần ủy thác cho một công trình awaitable:

def __await__(self): 
    return new_sleep().__await__() 

này hoạt động bởi vì phương thức __await__ trả về một trình lặp (xem PEP 492), vì vậy việc trả về trình lặp vòng của __await__ là tốt.

Điều này có nghĩa là, tất nhiên, chúng tôi không thể thay đổi hành vi tạm ngưng của bản gốc đang chờ. Cách tiếp cận tổng quát hơn là phản ánh từ khóa await và sử dụng yield from - điều này cho phép chúng ta kết hợp nhiều vòng lặp awaitables' thành một:

def __await__(self): 
    # theoretically possible, but not useful for my example: 
    #yield from something_else_first().__await__() 
    yield from new_sleep().__await__() 

Dưới đây là đánh bắt: đây không phải là làm chính xác những điều tương tự như phiên bản đầu tiên!yield from là một biểu hiện, vì vậy để thực hiện chính xác giống như trước, chúng ta cũng cần phải trở về giá trị mà:

def __await__(self): 
    return (yield from new_sleep().__await__()) 

này trực tiếp phản ánh cách chúng tôi sẽ viết Đoàn thích hợp bằng cách sử dụng await cú pháp:

return await new_sleep() 

bit phụ - sự khác biệt giữa hai loại này là gì?

def __await__(self): 
    do_something_synchronously() 
    return new_sleep().__await__() 

def __await__(self): 
    do_something_synchronously() 
    return (yield from new_sleep().__await__()) 

Biến thể đầu tiên là hàm thuần: khi bạn gọi nó, do_... được thực thi và một trình lặp được trả về. Thứ hai là một chức năng máy phát điện; gọi nó không thực thi bất kỳ mã nào của chúng tôi! Chỉ khi trình vòng lặp được trả về được sinh ra lần đầu tiên thì do_... sẽ được thực thi. Điều này tạo nên sự khác biệt trong những điều sau đây, một tình huống ít gây tranh cãi:

def foo(): 
    tmp = Waiting.__await__() 
    do_something() 
    yield from tmp 
Các vấn đề liên quan