# One More Thing (Python)...

In our experience, few things enhance understanding of programming techniques than comparing similar implementations across languages. Now that we have seen how Go would do it, let's take a look at the implementation in Python. This one is especially interesting because Python (3.5+) added support to async/await, and it is quite similar to what we have in Node.

As a reminder, in this example we are:

1. Looking up a list of authors for the Microservices Architecture book, using Google Books API
2. Looking up, in parallel, the number of books each author has published. This means issuing several concurrent API calls to Google books API, all at the same time.
3. Waiting until all parallel lookups from #2 finish, since they can take different length of time, and when all of them are finished - printing out the results in a nicely-formatted way.

## Chain Parallel with Sequential

```python
import aiohttp
import asyncio
import json
import uvloop # optional. @see https://github.com/MagicStack/uvloop

base_url = "https://www.googleapis.com/books/v1/volumes?q="

async def fetch(url):
  '''
  fetches contents of a URL asynchronously
  '''
  async with aiohttp.ClientSession() as session:
    async with session.get(url) as response:
      return await response.text()

async def book_authors(isbn):
  '''
  returns all authors of a book
  '''
  book_url = f"{base_url}isbn:{isbn}" # "Microservice Architecture"
  book_info = await fetch(book_url)
  json_response = json.loads(book_info)
  authors = json_response['items'][0]['volumeInfo']['authors']
  return authors

async def authorNumBooks(author_name):
  '''
  returns number of books published by an author
  '''
  author_url = f"{base_url}inauthor:{author_name}"
  print(f"Quering: {author_name}")
  author_info = await fetch(author_url)
  print(f"Fetched: {author_name}")
  author_info_json = json.loads(author_info)
  count = author_info_json['totalItems']
  return {"name" : author_name, "count": count}

async def msa_authors():
  authors = await book_authors(1491956224) # "Microservice Architecture"
  futures = [authorNumBooks(author) for author in authors]

  resolved_infos = await asyncio.gather(*futures)
  return resolved_infos

async def main():
    author_stat_results = await msa_authors()
    print("==== Author stats results: ====")
    pp = [f"  {item['name']} : {item['count']}" for item in author_stat_results]
    print('\n'.join(pp))

if __name__ == '__main__':
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) # optional: higher performance
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())


# @see: https://github.com/timofurrer/awesome-asyncio
```

> Side note: full source code of this example can be found at:\
> <https://github.com/inadarei/python-async>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.promises123.com/one-more-thing-python.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
