Witaj w Module 6, @name! Darwin tutaj z ekscytującym tematem - asynchroniczne programowanie w Pythonie! ⚡🐍
Do tej pory wszystkie Twoje programy były synchroniczne - wykonywały operacje jedna po drugiej. Teraz poznasz asynchroniczność - technikę pozwalającą na wykonywanie wielu zadań jednocześnie! 🚀
Analogia Safari: Synchroniczne programowanie to jak jeden przewodnik obserwujący zwierzęta po kolei - najpierw lwy (czeka 30 minut), potem słonie (czeka 20 minut), potem gepardy (czeka 15 minut). Asynchroniczne to jak wielu przewodników obserwujących jednocześnie - jeden przy lwach, drugi przy słoniach, trzeci przy gepardach - wszyscy równolegle! ⏱️🦁🐘🐆
1import time
2
3def observe_lions():
4 """Obserwacja lwów - trwa 3 sekundy"""
5 print("🦁 Rozpoczynam obserwację lwów...")
6 time.sleep(3) # Symulacja czekania
7 print("🦁 Obserwacja lwów zakończona!")
8 return {"species": "Lew", "count": 5}
9
10def observe_elephants():
11 """Obserwacja słoni - trwa 2 sekundy"""
12 print("🐘 Rozpoczynam obserwację słoni...")
13 time.sleep(2)
14 print("🐘 Obserwacja słoni zakończona!")
15 return {"species": "Słoń", "count": 3}
16
17def observe_cheetahs():
18 """Obserwacja gepardów - trwa 4 sekundy"""
19 print("🐆 Rozpoczynam obserwację gepardów...")
20 time.sleep(4)
21 print("🐆 Obserwacja gepardów zakończona!")
22 return {"species": "Gepard", "count": 2}
23
24# Synchroniczne wykonanie - PO KOLEI
25start = time.time()
26result1 = observe_lions() # Czeka 3s
27result2 = observe_elephants() # Potem czeka 2s
28result3 = observe_cheetahs() # Potem czeka 4s
29end = time.time()
30
31print(f"\nCałkowity czas: {end - start:.1f}s")
32# Output: Całkowity czas: 9.0s (3 + 2 + 4)Problem: Czas = 9 sekund! Musimy czekać na każdą obserwację po kolei, mimo że moglibyśmy obserwować równolegle! ⏰❌
1import asyncio
2
3async def observe_lions():
4 """Asynchroniczna obserwacja lwów"""
5 print("🦁 Rozpoczynam obserwację lwów...")
6 await asyncio.sleep(3) # await zamiast time.sleep!
7 print("🦁 Obserwacja lwów zakończona!")
8 return {"species": "Lew", "count": 5}
9
10async def observe_elephants():
11 """Asynchroniczna obserwacja słoni"""
12 print("🐘 Rozpoczynam obserwację słoni...")
13 await asyncio.sleep(2)
14 print("🐘 Obserwacja słoni zakończona!")
15 return {"species": "Słoń", "count": 3}
16
17async def observe_cheetahs():
18 """Asynchroniczna obserwacja gepardów"""
19 print("🐆 Rozpoczynam obserwację gepardów...")
20 await asyncio.sleep(4)
21 print("🐆 Obserwacja gepardów zakończona!")
22 return {"species": "Gepard", "count": 2}
23
24async def main():
25 """Główna funkcja async"""
26 start = time.time()
27
28 # Wykonaj równolegle!
29 results = await asyncio.gather(
30 observe_lions(),
31 observe_elephants(),
32 observe_cheetahs()
33 )
34
35 end = time.time()
36 print(f"\nCałkowity czas: {end - start:.1f}s")
37 # Output: Całkowity czas: 4.0s (max z 3, 2, 4)
38 print(f"Wyniki: {results}")
39
40# Uruchom async main
41asyncio.run(main())Wynik: Czas = 4 sekundy! Wszystkie obserwacje wykonują się równolegle! ⚡✅
Oszczędność: 9s → 4s = 5 sekund szybciej (55% redukcja)!
1# Synchroniczna funkcja
2def sync_function():
3 return "Sync result"
4
5# Asynchroniczna funkcja (coroutine)
6async def async_function():
7 return "Async result"
tworzy coroutine - specjalną funkcję, która może być "zawieszona" (async def
await) i wznowiona później.1async def fetch_species_data(species_id):
2 print(f"Pobieranie danych dla gatunku {species_id}...")
3 await asyncio.sleep(2) # Symulacja I/O operation
4 return {"id": species_id, "name": "Lew", "population": 120}
5
6async def main():
7 # await zawiesza wykonanie dopóki nie otrzyma wyniku
8 data = await fetch_species_data(1)
9 print(f"Otrzymano dane: {data}")
10
11asyncio.run(main())
mówi: "Czekaj na wynik, ale w międzyczasie pozwól innym zadaniom działać".await
⚠️ WAŻNE:
await można używać tylko wewnątrz async def!Event loop to serce asynchroniczności - zarządza wszystkimi coroutines i przełącza między nimi.
Analogia: Event loop to jak dispatcher safari - przydziela przewodników do różnych zadań, przełącza między nimi, i zbiera wyniki! 🎯
1import asyncio
2
3async def task1():
4 print("Task 1 start")
5 await asyncio.sleep(1)
6 print("Task 1 end")
7
8async def task2():
9 print("Task 2 start")
10 await asyncio.sleep(0.5)
11 print("Task 2 end")
12
13async def main():
14 # Event loop zarządza tymi taskami
15 await asyncio.gather(task1(), task2())
16
17# asyncio.run() tworzy event loop i wykonuje main()
18asyncio.run(main())Output:
1Task 1 start
2Task 2 start
3Task 2 end (po 0.5s)
4Task 1 end (po 1s)Event loop przełączał się między task1 i task2!
asyncio.gather() wykonuje wiele coroutines równolegle i zwraca listę wyników:1import asyncio
2
3async def get_species(species_id):
4 await asyncio.sleep(1)
5 return {"id": species_id, "name": f"Species {species_id}"}
6
7async def main():
8 # Pobierz 5 gatunków równolegle
9 results = await asyncio.gather(
10 get_species(1),
11 get_species(2),
12 get_species(3),
13 get_species(4),
14 get_species(5)
15 )
16
17 print(f"Pobrano {len(results)} gatunków:")
18 for species in results:
19 print(f" - {species['name']}")
20
21asyncio.run(main())Czas: 1 sekunda (zamiast 5 sekund synchronicznie)! ⚡
zwraca wyniki w tej samej kolejności, w jakiej podałeś coroutines!gather()
create_task() uruchamia coroutine w tle (nie czeka na wynik):1async def background_observation(species):
2 print(f"🔍 Rozpoczynam obserwację {species}...")
3 await asyncio.sleep(3)
4 print(f"✅ Obserwacja {species} zakończona!")
5
6async def main():
7 # Uruchom w tle
8 task1 = asyncio.create_task(background_observation("Lew"))
9 task2 = asyncio.create_task(background_observation("Słoń"))
10
11 print("Robię inne rzeczy w międzyczasie...")
12 await asyncio.sleep(1)
13 print("Nadal robię inne rzeczy...")
14
15 # Czekaj na zakończenie tasków
16 await task1
17 await task2
18
19asyncio.run(main())Output:
1🔍 Rozpoczynam obserwację Lew...
2🔍 Rozpoczynam obserwację Słoń...
3Robię inne rzeczy w międzyczasie...
4Nadal robię inne rzeczy...
5✅ Obserwacja Lew zakończona!
6✅ Obserwacja Słoń zakończona!Python pozwala na async list/dict comprehensions:
1async def get_population(species_id):
2 await asyncio.sleep(0.1)
3 return species_id * 10
4
5async def main():
6 # Async list comprehension
7 populations = [await get_population(i) for i in range(1, 6)]
8 print(f"Populacje: {populations}")
9 # Output: Populacje: [10, 20, 30, 40, 50]
10
11 # Ale to wykonuje się SEKWENCYJNIE!
12 # Lepiej użyć gather():
13 populations = await asyncio.gather(
14 *[get_population(i) for i in range(1, 6)]
15 )
16 print(f"Populacje (równolegle): {populations}")
17
18asyncio.run(main())Asynchroniczny klient do API Safari:
1import asyncio
2import aiohttp # pip install aiohttp
3
4class SafariAPIClient:
5 def __init__(self, base_url: str):
6 self.base_url = base_url
7
8 async def get_species(self, species_id: int):
9 """Pobierz dane gatunku"""
10 async with aiohttp.ClientSession() as session:
11 async with session.get(f"{self.base_url}/species/{species_id}") as response:
12 return await response.json()
13
14 async def get_multiple_species(self, species_ids: list[int]):
15 """Pobierz wiele gatunków równolegle"""
16 async with aiohttp.ClientSession() as session:
17 tasks = []
18 for species_id in species_ids:
19 task = session.get(f"{self.base_url}/species/{species_id}")
20 tasks.append(task)
21
22 responses = await asyncio.gather(*tasks)
23 results = []
24 for response in responses:
25 data = await response.json()
26 results.append(data)
27
28 return results
29
30async def main():
31 client = SafariAPIClient("https://api.safari-db.com")
32
33 # Pobierz 10 gatunków równolegle
34 species_ids = list(range(1, 11))
35 start = time.time()
36 results = await client.get_multiple_species(species_ids)
37 end = time.time()
38
39 print(f"Pobrano {len(results)} gatunków w {end - start:.2f}s")
40
41asyncio.run(main())Synchronicznie: 10 requests × 0.5s = 5 sekund Asynchronicznie: max(0.5s) = 0.5 sekundy ⚡
Dla CPU-bound użyj multiprocessing zamiast async!
1class AsyncDatabaseConnection:
2 async def __aenter__(self):
3 print("Łączenie z bazą...")
4 await asyncio.sleep(1)
5 return self
6
7 async def __aexit__(self, exc_type, exc_val, exc_tb):
8 print("Zamykanie połączenia...")
9 await asyncio.sleep(0.5)
10
11 async def query(self, sql):
12 print(f"Wykonuję: {sql}")
13 await asyncio.sleep(0.2)
14 return [{"id": 1, "name": "Lew"}]
15
16async def main():
17 async with AsyncDatabaseConnection() as db:
18 results = await db.query("SELECT * FROM species")
19 print(f"Wyniki: {results}")
20
21asyncio.run(main())1async def risky_operation(species_id):
2 if species_id == 3:
3 raise ValueError(f"Gatunek {species_id} nie istnieje!")
4 await asyncio.sleep(1)
5 return {"id": species_id}
6
7async def main():
8 try:
9 results = await asyncio.gather(
10 risky_operation(1),
11 risky_operation(2),
12 risky_operation(3), # Błąd!
13 return_exceptions=True # Zwróć wyjątki zamiast rzucać
14 )
15
16 for i, result in enumerate(results, 1):
17 if isinstance(result, Exception):
18 print(f"Gatunek {i}: BŁĄD - {result}")
19 else:
20 print(f"Gatunek {i}: OK - {result}")
21
22 except Exception as e:
23 print(f"Błąd: {e}")
24
25asyncio.run(main())Output:
1Gatunek 1: OK - {'id': 1}
2Gatunek 2: OK - {'id': 2}
3Gatunek 3: BŁĄD - Gatunek 3 nie istnieje!W tej lekcji nauczyłeś/aś się:
async def i await - składniaasyncio.gather() - równoległe wykonanieasyncio.create_task() - taski w tleAnalogia Safari finalna: Async to jak wielu przewodników safari obserwujących różne gatunki jednocześnie - zamiast czekać 9 sekund obserwując po kolei (sync), obserwują równolegle i kończą w 4 sekundy (async)! Event loop to dispatcher koordynujący wszystkich przewodników! ⏱️🦁🐘🐆
Następna lekcja: Darwin pokaże Ci FastAPI - nowoczesny framework async do budowania błyskawicznych API! 🚀🌐