partial

October 8, 2025


understanding partial() with async/futures

the basic idea

partial "freezes" args in a fn so you don't have to pass them every time

from functools import partial def add(a, b, c): return a + b + c add_5_and_10 = partial(add, 5, 10) add_5_and_10(3) # returns 18 (same as add(5, 10, 3))

the problem: fetching from multiple APIs

imagine you need to fetch user data from 3 different API endpoints at the same time

here's the messy way:

import asyncio from functools import partial from concurrent.futures import ThreadPoolExecutor def fetch_data(user_id, api_endpoint, timeout=30, retry=3, api_key="secret"): return f"Data from {api_endpoint} for user {user_id}" async def get_user_data_messy(user_id): executor = ThreadPoolExecutor() loop = asyncio.get_event_loop() # repetition future1 = loop.run_in_executor( executor, lambda: fetch_data(user_id, "profile", 30, 3, "secret") ) future2 = loop.run_in_executor( executor, lambda: fetch_data(user_id, "orders", 30, 3, "secret") ) future3 = loop.run_in_executor( executor, lambda: fetch_data(user_id, "reviews", 30, 3, "secret") ) results = await asyncio.gather(future1, future2, future3) return results

the clean way with partial:

async def get_user_data_clean(user_id): executor = ThreadPoolExecutor() loop = asyncio.get_event_loop() # common way fetcher = partial( fetch_data, user_id=user_id, timeout=30, retry=3, api_key="secret" ) endpoints = ["profile", "orders", "reviews"] futures = [ loop.run_in_executor(executor, partial(fetcher, api_endpoint=ep)) for ep in endpoints ] results = await asyncio.gather(*futures) return results

why the double partial

loop.run_in_executor(executor, partial(fetcher, api_endpoint=ep))

here's what's actually happening:

# first partial: lock in the common stuff fetcher = partial(fetch_data, user_id=user_id, timeout=30, retry=3, api_key="secret") # second partial: add the specific endpoint profile_fetcher = partial(fetcher, api_endpoint="profile") # now profile_fetcher() is a zero-argument callable # calling it is the same as: fetch_data(user_id, "profile", 30, 3, "secret")

seeing it run

import time def fetch_data(user_id, api_endpoint, timeout=30, retry=3, api_key="secret"): time.sleep(1) # pretend this is an API call return f"Data from {api_endpoint} for user {user_id}" async def main(): start = time.time() results = await get_user_data_clean(12345) print(f"completed in {time.time() - start:.2f}s") print(results) # completed in 1.01s (all 3 APIs ran at the same time) # ['Data from profile for user 12345', # 'Data from orders for user 12345', # 'Data from reviews for user 12345'] asyncio.run(main())