Ачааллж байна...
Түвшин 4 дуусч байна. Одоо бид жинхэнэ мэргэжлийн Python төсөл бичнэ.
Тэр нь:
Жинхэнэ API-аас өгөгдөл татна — Open-Meteo, түлхүүр шаардахгүй
ОЛОН ФАЙЛТАЙ — package бүтэцтэй, үүргээр ангилагдсан
Бүрэн type hint-тэй — dataclass -ууд
Кэштэй — ижил өгөгдлийг дахин татахгүй
Алдаанд тэсвэртэй — сүлжээ тасарсан ч эвлэгээр зогсоно
Түвшин 4-ийн БҮХ мэдлэг нэгдэнэ: decorator (Бүлэг 10), HTTP ба requests (Бүлэг 11), package ба модуль (Бүлэг 12).
Мөн өмнөх түвшнүүд ч бас: dataclass (Бүлэг 9), JSON (Бүлэг 6), type hint (Бүлэг 4), comprehension ба zip (Бүлэг 2).
Энэ бол гэрийн даалгавар БИШ. Бид түүнийг файл файлаар нь, хамтдаа барина.
Алхам 1. Шинэ хавтас үүсгэнэ үү: tsag-agaar-app.
Алхам 2. PowerShell нээж, venv үүсгээд идэвхжүүлнэ:
powershell
python -m venv venv
.\venv\Scripts\Activate.ps1(venv) гарч ирснийг баталгаажуулна уу.
Алхам 3. requests -ыг суулгана:
powershell
pip install requestsАлхам 4. Шаардлагаа тэмдэглэнэ:
powershell
pip freeze > requirements.txtАлхам 5. VS Code -оор дараах бүтцийг үүсгэнэ үү:
tsag-agaar-app/
├── venv/
├── requirements.txt
├── main.py
└── tsag_agaar/
├── __init__.py
├── config.py
├── models.py
├── api.py
├── cache.py
├── display.py
└── decorators.pyХавтас үүсгэхдээ VS Code-ын file explorer -ийг ашиглаарай — PowerShell -ээс илүү найдвартай.
Код бичихээсээ өмнө файл бүрийн ҮҮРГИЙГ тодорхойлъё (Бүлэг 12.2-ын зарчим).
Файл | Үүрэг | Юуг МЭДЭХГҮЙ |
|---|---|---|
| Тогтмолууд, URL, timeout | Бүх зүйл |
| Dataclass-ууд | API, файл, хэрэглэгч |
| Цаг хэмжигч | Домэйн логик |
| Интернэттэй ярина | Хэрэглэгч, файл |
| Файлд хадгална | API, хэрэглэгч |
| Харуулна | API, файл |
| Хэрэглэгчтэй ярина |
|
Гол зарчим: файл бүр НЭГ ЗОРИЛГОТОЙ.
Хамаарлын чиглэл (import нь ЭНЭ ЧИГЛЭЛД урсана):
main.py
↓
display.py, api.py, cache.py
↓
models.py, decorators.py
↓
config.pyЭСРЭГ чиглэлд ХЭЗЭЭ Ч import хийхгүй. Тэр нь дугуй import үүсгэнэ (Бүлэг 12.1).
Жишээ нь api.py нь display.py -г import ХИЙХГҮЙ. Учир нь API нь харуулах талаар юу ч мэдэх ЁСГҮЙ.
config.pypython
# tsag_agaar/config.py
# Бүх тогтмолууд НЭГ газарт
# ─── API ─────────────────────────────────────────────────────
FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
GEOCODE_URL = "https://geocoding-api.open-meteo.com/v1/search"
TIMEOUT = 10
OROLDLOGO = 3
ODOOGIIN_UZUULELT = "temperature_2m,wind_speed_10m,relative_humidity_2m"
UDUR_TUTMIIN_UZUULELT = "temperature_2m_max,temperature_2m_min,precipitation_sum"
URIDCHILSAN_UDUR = 7
# ─── КЭШ ─────────────────────────────────────────────────────
KESH_ZAM = "kesh.json"
KESH_KHUCHINTEI_SEKUND = 600 # 10 минут
# ─── ХАРУУЛАХ ────────────────────────────────────────────────
URUGN = 56Бүх "ид шидийн тоо" нь энд байна. Timeout өөрчлөх хэрэгтэй бол — нэг мөр засна.
KESH_KHUCHINTEI_SEKUND = 600 — кэш 10 минут хүчинтэй. Түүнээс хойш дахин татна. Цаг агаар 10 минут тутамд өөрчлөгддөггүй тул тэр нь боломжийн.
models.pypython
# tsag_agaar/models.py
# Өгөгдлийн бүтцүүд
from dataclasses import dataclass
@dataclass(frozen=True)
class Khot:
ner: str
uls: str
lat: float
lon: float
def __str__(self) -> str:
return f"{self.ner}, {self.uls}"
@classmethod
def from_api(cls, d: dict) -> "Khot":
return cls(
ner=d["name"],
uls=d.get("country", "тодорхойгүй"),
lat=d["latitude"],
lon=d["longitude"],
)
@dataclass(frozen=True)
class OdoогийнTsagAgaar:
tsag: str
temperatur: float
salkhi: float
chiiglegdel: int
@classmethod
def from_api(cls, d: dict) -> "OdoогийнTsagAgaar":
return cls(
tsag=d["time"],
temperatur=d["temperature_2m"],
salkhi=d["wind_speed_10m"],
chiiglegdel=d["relative_humidity_2m"],
)
@dataclass(frozen=True)
class UdruinTsagAgaar:
ognoo: str
ikh: float
bag: float
khur_tunadas: float
@property
def zuruu(self) -> float:
return self.ikh - self.bag
@property
def khur_uu(self) -> bool:
return self.khur_tunadas > 0.1
@dataclass(frozen=True)
class TsagAgaariinMedee:
khot: Khot
odoo: OdoогийнTsagAgaar
udruud: list[UdruinTsagAgaar]
@property
def dundaj_ikh(self) -> float:
if not self.udruud:
return 0.0
return sum(u.ikh for u in self.udruud) / len(self.udruud)
@property
def khurtai_udur(self) -> int:
return sum(1 for u in self.udruud if u.khur_uu)Бүлэг 9-ийн БҮХ мэдлэг ажиллаж байна.
@dataclass(frozen=True) — immutable (Бүлэг 9.1). Эдгээр нь ӨГӨГДӨЛ, төлөв биш.
@classmethod from_api (Бүлэг 9.3) — API-гийн dictionary -ээс объект үүсгэх ГҮҮР. cls нь self -ийн class хувилбар.
d.get("country", "тодорхойгүй") (Бүлэг 4.3) — API нь заримдаа улсыг оруулдаггүй.
@property (Бүлэг 8.4) — тооцоологдсон утгууд. Хадгалагдаагүй тул ХЭЗЭЭ Ч хуучрахгүй.
sum(1 for u in self.udruud if u.khur_uu) — тоолох гоё арга (comprehension, Бүлэг 2.1).
Анзаараарай: эдгээр class-ууд нь requests -ийн талаар ЮУ Ч МЭДЭХГҮЙ. Тэдгээр нь зөвхөн өгөгдөл. Тэр нь зөв.
decorators.pypython
# tsag_agaar/decorators.py
# Туслах decorator-ууд
import functools
import time
from typing import Any, Callable
def khugatsaa_khemjikh(func: Callable) -> Callable:
@functools.wraps(func)
def dotood(*args: Any, **kwargs: Any) -> Any:
ekhelsen = time.time()
ur_dun = func(*args, **kwargs)
duussan = time.time()
print(f" [{func.__name__}: {duussan - ekhelsen:.2f}с]")
return ur_dun
return dotoodБүлэг 10.3-ын цаг хэмжигч.
@functools.wraps(func) — эх функцийн нэрийг хамгаална (Бүлэг 10.2).
*args, **kwargs — БҮХ аргументыг дамжуулна (Бүлэг 3.3).
return ur_dun — буцаалтыг дамжуулна. Хамгийн түгээмэл алдаа (Бүлэг 10.2).
Энэ файл нь домэйн логикийн талаар ЮУ Ч мэдэхгүй. Тэр нь ЯМАР Ч функц дээр ажиллана.
api.pypython
# tsag_agaar/api.py
# Open-Meteo API-тай харилцах
import time
import requests
from tsag_agaar.config import (
FORECAST_URL,
GEOCODE_URL,
ODOOGIIN_UZUULELT,
OROLDLOGO,
TIMEOUT,
UDUR_TUTMIIN_UZUULELT,
URIDCHILSAN_UDUR,
)
from tsag_agaar.decorators import khugatsaa_khemjikh
from tsag_agaar.models import (
Khot,
OdoогийнTsagAgaar,
TsagAgaariinMedee,
UdruinTsagAgaar,
)
class ApiAlda(Exception):
"""API-тай холбоотой алдаа."""
def _khuselt(url: str, parametruud: dict) -> dict:
"""Хамгаалалттай HTTP хүсэлт. Амжилтгүй бол ApiAlda гаргана."""
for i in range(1, OROLDLOGO + 1):
try:
hariult = requests.get(url, params=parametruud, timeout=TIMEOUT)
hariult.raise_for_status()
return hariult.json()
except requests.exceptions.HTTPError as alda:
kod = alda.response.status_code
if 400 <= kod < 500:
raise ApiAlda(f"Хүсэлт буруу байна ({kod}).") from alda
print(f" Серверийн алдаа ({kod}). Оролдлого {i}/{OROLDLOGO}")
except (
requests.exceptions.Timeout,
requests.exceptions.ConnectionError,
):
print(f" Сүлжээний алдаа. Оролдлого {i}/{OROLDLOGO}")
except requests.exceptions.JSONDecodeError as alda:
raise ApiAlda("Серверийн хариу JSON биш байна.") from alda
if i < OROLDLOGO:
khuleelt = 2**i
print(f" {khuleelt} секунд хүлээж байна...")
time.sleep(khuleelt)
raise ApiAlda(f"{OROLDLOGO} удаа оролдсон ч холбогдож чадсангүй.")
@khugatsaa_khemjikh
def khot_khaikh(ner: str) -> Khot | None:
parametruud = {"name": ner, "count": 1, "language": "en"}
uguudul = _khuselt(GEOCODE_URL, parametruud)
ur_dun = uguudul.get("results")
if not ur_dun:
return None
return Khot.from_api(ur_dun[0])
@khugatsaa_khemjikh
def medee_avakh(khot: Khot) -> TsagAgaariinMedee:
parametruud = {
"latitude": khot.lat,
"longitude": khot.lon,
"current": ODOOGIIN_UZUULELT,
"daily": UDUR_TUTMIIN_UZUULELT,
"timezone": "auto",
"forecast_days": URIDCHILSAN_UDUR,
}
uguudul = _khuselt(FORECAST_URL, parametruud)
odoo = OdoогийнTsagAgaar.from_api(uguudul["current"])
udur_tutmiin = uguudul["daily"]
udruud = [
UdruinTsagAgaar(
ognoo=ognoo,
ikh=ikh,
bag=bag,
khur_tunadas=khur,
)
for ognoo, ikh, bag, khur in zip(
udur_tutmiin["time"],
udur_tutmiin["temperature_2m_max"],
udur_tutmiin["temperature_2m_min"],
udur_tutmiin["precipitation_sum"],
)
]
return TsagAgaariinMedee(khot=khot, odoo=odoo, udruud=udruud)
# ─── Тусад нь турших ─────────────────────────────────────────
if __name__ == "__main__":
print("api.py -ийг турших...\n")
khot = khot_khaikh("Ulaanbaatar")
if khot is None:
print("Хот олдсонгүй")
else:
print(f"Хот: {khot}")
medee = medee_avakh(khot)
print(f"Одоо: {medee.odoo.temperatur}°C")
print(f"Дундаж их: {medee.dundaj_ikh:.1f}°C")
print(f"Хуртай өдөр: {medee.khurtai_udur}")Хамгийн чухал файл. Задалъя.
class ApiAlda(Exception): — өөрийн алдааны class!
Бүлэг 7.1-ийн inheritance ажиллаж байна. Exception -ээс өвлөж байна.
Яагаад? Учир нь main.py нь requests -ийн талаар МЭДЭХ ЁСГҮЙ. Хэрэв api.py нь requests.exceptions.ConnectionError гаргавал — main.py нь requests -ыг import хийх хэрэгтэй болно. Салгалт эвдэрнэ.
Оронд нь api.py нь БҮХ сүлжээний алдааг барьж, өөрийн ApiAlda болгож хувиргана. main.py нь зөвхөн ApiAlda -г мэднэ.
(raise ба өөрийн exception -ыг Бүлэг 13.2-т бүрэн үзнэ. Одоохондоо "алдаа зориудаар үүсгэх" гэж ойлгоод өнгөрөөрэй.)
_khuselt — доогуур зураастай нэр (Бүлэг 8.4-өөс): "энэ бол ДОТООД функц, гаднаас дуудах ёсгүй."
Тэр нь Бүлэг 11.5-ын БҮХ алдаа зохицуулалтыг агуулна: retry, exponential backoff, 4xx vs 5xx ялгаа, timeout.
raise ... from alda — "энэ алдаа нь ТЭР алдаанаас үүдэлтэй." Дебаг хийхэд хэрэгтэй.
@khugatsaa_khemjikh — decorator ажиллаж байна (Бүлэг 10.3). Одоо хүсэлт бүр хэдэн секунд болсныг харна.
zip доторх comprehension — Бүлэг 2.1 ба 2.3 нэгдэж байна. API нь ЗЭРЭГЦЭЭ ЖАГСААЛТ буцаадаг (Бүлэг 11.4), zip нь тэднийг зэрэгцүүлж, comprehension нь объект болгож байна.
if __name__ == "__main__": — тусад нь турших (Бүлэг 12.3).
Туршиж үзье:
powershell
python -m tsag_agaar.apiapi.py -ийг турших...
[khot_khaikh: 0.43с]
Хот: Ulaanbaatar, Mongolia
[medee_avakh: 0.31с]
Одоо: 18.7°C
Дундаж их: 25.1°C
Хуртай өдөр: 3api.py -г ТУСАД НЬ туршлаа. main.py шаардлагагүй.
cache.pyОдоо ухаалаг зүйл нэмье: кэш.
Хэрэв та ижил хотыг 10 минутын дотор хоёр удаа асуувал — API-г дахин дуудах шаардлагагүй. Хадгалсан хариуг ашигла.
python
# tsag_agaar/cache.py
# Хариултыг файлд кэшлэх
import json
import time
from dataclasses import asdict
from tsag_agaar.config import KESH_KHUCHINTEI_SEKUND, KESH_ZAM
from tsag_agaar.models import (
Khot,
OdoогийнTsagAgaar,
TsagAgaariinMedee,
UdruinTsagAgaar,
)
def _achaalakh() -> dict:
try:
with open(KESH_ZAM, "r", encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
return {}
except json.JSONDecodeError:
print(" Кэш файл эвдэрсэн байна. Шинээр эхэлье.")
return {}
def _khadgalakh(kesh: dict) -> None:
with open(KESH_ZAM, "w", encoding="utf-8") as f:
json.dump(kesh, f, ensure_ascii=False, indent=2)
def _medee_to_dict(medee: TsagAgaariinMedee) -> dict:
return {
"khot": asdict(medee.khot),
"odoo": asdict(medee.odoo),
"udruud": [asdict(u) for u in medee.udruud],
}
def _medee_from_dict(d: dict) -> TsagAgaariinMedee:
return TsagAgaariinMedee(
khot=Khot(**d["khot"]),
odoo=OdoогийнTsagAgaar(**d["odoo"]),
udruud=[UdruinTsagAgaar(**u) for u in d["udruud"]],
)
def avakh(khotiin_ner: str) -> TsagAgaariinMedee | None:
"""Кэшээс мэдээ ав. Байхгүй эсвэл хуучирсан бол None."""
kesh = _achaalakh()
tulkhuur = khotiin_ner.strip().lower()
bichlelg = kesh.get(tulkhuur)
if bichlelg is None:
return None
nas = time.time() - bichlelg["tsag"]
if nas > KESH_KHUCHINTEI_SEKUND:
return None
print(f" [кэшээс, {int(nas)}с хуучин]")
try:
return _medee_from_dict(bichlelg["medee"])
except (KeyError, TypeError):
return None
def khadgalakh(khotiin_ner: str, medee: TsagAgaariinMedee) -> None:
kesh = _achaalakh()
tulkhuur = khotiin_ner.strip().lower()
kesh[tulkhuur] = {
"tsag": time.time(),
"medee": _medee_to_dict(medee),
}
_khadgalakh(kesh)Бүлэг 6 ба 9.3-ын мэдлэг нэгдэж байна.
asdict(medee.khot) (Бүлэг 9.3) — dataclass → dictionary.
Khot(**d["khot"]) (Бүлэг 3.3, 9.3) — dictionary → dataclass. Хоёр од нь ЗАДАЛНА.
_medee_to_dict ба _medee_from_dict — ГҮҮР. Бүлэг 9.3-т сурсанчлан: asdict нь гүнзгий ажилладаг, гэвч эсрэг зам нь ГАРААР угсрахыг шаарддаг.
time.time() — одоогийн цаг (секундээр). Кэшийн НАСЫГ тооцоолоход ашиглана.
tulkhuur = khotiin_ner.strip().lower() — Бүлэг 6.4-ийн НОРМЧЛОЛ. "Ulaanbaatar", "ULAANBAATAR", " ulaanbaatar " — бүгд ижил key.
display.pypython
# tsag_agaar/display.py
# Дэлгэц дээр харуулах
from tsag_agaar.config import URUGN
from tsag_agaar.models import TsagAgaariinMedee
def medee_kheveleкh(medee: TsagAgaariinMedee) -> None:
khot = medee.khot
odoo = medee.odoo
print()
print("=" * URUGN)
print(f"{str(khot).upper()}")
print("=" * URUGN)
print(f"Координат: {khot.lat:.4f}, {khot.lon:.4f}")
print()
print("ОДООГИЙН БАЙДАЛ")
print("-" * URUGN)
print(f" Цаг: {odoo.tsag}")
print(f" Температур: {odoo.temperatur:>6.1f}°C")
print(f" Салхи: {odoo.salkhi:>6.1f} км/ц")
print(f" Чийгшилт: {odoo.chiiglegdel:>6}%")
print()
if not medee.udruud:
print("=" * URUGN)
return
print(f"{len(medee.udruud)} ХОНОГИЙН УРЬДЧИЛСАН МЭДЭЭ")
print("-" * URUGN)
print(f" {'ОГНОО':<12} {'БАГА':>7} {'ИХ':>7} {'ЗӨРҮҮ':>7} {'ХУР':>8}")
print("-" * URUGN)
for u in medee.udruud:
khur = f"{u.khur_tunadas:.1f}мм" if u.khur_uu else "—"
print(
f" {u.ognoo:<12} {u.bag:>6.1f}° {u.ikh:>6.1f}° "
f"{u.zuruu:>6.1f}° {khur:>8}"
)
print("-" * URUGN)
print(f" Дундаж их температур: {medee.dundaj_ikh:.1f}°C")
print(f" Хур тунадастай өдөр: {medee.khurtai_udur} / {len(medee.udruud)}")
print("=" * URUGN)
def alda_kheveleкh(mesej: str) -> None:
print(f"\n[Алдаа] {mesej}")
def medegdel(mesej: str) -> None:
print(f" {mesej}")Энэ файл нь requests, json, файлын талаар ЮУ Ч мэдэхгүй. Тэр зөвхөн TsagAgaariinMedee объектыг авч, ХАРУУЛНА.
Бүлэг 6.4-ийн формат заагчид ажиллаж байна: {:<12}, {:>6.1f}.
medee.dundaj_ikh, medee.khurtai_udur, u.zuruu, u.khur_uu — бүгд property (Бүлэг 8.4). Тооцоолол нь models.py -д амьдарч байна, display.py -д биш. Тэр нь зөв.
__init__.pypython
# tsag_agaar/__init__.py
# Package-ийн нийтийн интерфейс
from tsag_agaar.api import ApiAlda, khot_khaikh, medee_avakh
from tsag_agaar.display import alda_kheveleкh, medee_kheveleкh, medegdel
from tsag_agaar.models import (
Khot,
OdoогийнTsagAgaar,
TsagAgaariinMedee,
UdruinTsagAgaar,
)
import tsag_agaar.cache as cacheБүлэг 12.2-ын "ЦЭС". Package-ийн хэрэглэгч нь дотоод бүтцийг мэдэх шаардлагагүй.
main.pypython
# main.py
# Цаг агаарын программ — Түвшин 4 guided build
from tsag_agaar import (
ApiAlda,
alda_kheveleкh,
cache,
khot_khaikh,
medee_avakh,
medee_kheveleкh,
medegdel,
)
def medee_olokh(ner: str):
"""Кэшээс, эсвэл API-аас мэдээ ол."""
keshhees = cache.avakh(ner)
if keshhees is not None:
return keshhees
medegdel("API-аас татаж байна...")
khot = khot_khaikh(ner)
if khot is None:
return None
medee = medee_avakh(khot)
cache.khadgalakh(ner, medee)
return medee
def main() -> None:
print()
print("ЦАГ АГААРЫН ПРОГРАММ")
print("(гарахын тулд хоосон Enter дарна уу)")
while True:
print()
ner = input("Хотын нэр (англиар): ").strip()
if not ner:
print("\nБаяртай.")
break
try:
medee = medee_olokh(ner)
except ApiAlda as alda:
alda_kheveleкh(str(alda))
continue
if medee is None:
alda_kheveleкh(f"'{ner}' гэсэн хот олдсонгүй.")
continue
medee_kheveleкh(medee)
if __name__ == "__main__":
main()main.py нь БОГИНО. Бараг ямар ч логик байхгүй — тэр нь зөвхөн ЗОХИЦУУЛЖ байна.
import requests БАЙХГҮЙ. import json БАЙХГҮЙ. Тэдгээр нь main.py -гийн ажил биш.
except ApiAlda — өөрийн алдааны class -ыг барьж байна. requests -ийн алдаа энд хүрэхгүй.
powershell
python main.pyЦАГ АГААРЫН ПРОГРАММ
(гарахын тулд хоосон Enter дарна уу)
Хотын нэр (англиар): Ulaanbaatar
API-аас татаж байна...
[khot_khaikh: 0.41с]
[medee_avakh: 0.35с]
========================================================
ULAANBAATAR, MONGOLIA
========================================================
Координат: 47.9077, 106.8832
ОДООГИЙН БАЙДАЛ
--------------------------------------------------------
Цаг: 2026-07-13T22:00
Температур: 18.7°C
Салхи: 8.3 км/ц
Чийгшилт: 45%
7 ХОНОГИЙН УРЬДЧИЛСАН МЭДЭЭ
--------------------------------------------------------
ОГНОО БАГА ИХ ЗӨРҮҮ ХУР
--------------------------------------------------------
2026-07-13 12.4° 26.1° 13.7° —
2026-07-14 11.9° 24.8° 12.9° 2.3мм
2026-07-15 10.2° 22.0° 11.8° 5.1мм
2026-07-16 13.1° 25.3° 12.2° —
2026-07-17 14.8° 27.9° 13.1° —
2026-07-18 13.5° 26.4° 12.9° 1.2мм
2026-07-19 11.0° 23.1° 12.1° —
--------------------------------------------------------
Дундаж их температур: 25.1°C
Хур тунадастай өдөр: 3 / 7
========================================================
Хотын нэр (англиар): Ulaanbaatar
[кэшээс, 12с хуучин]
========================================================
ULAANBAATAR, MONGOLIA
========================================================
...ХОЁР ДАХЬ удаад — КЭШЭЭС. API дуудагдаагүй. Хугацааны хэмжилт ч байхгүй.
Кэш ажиллаж байна. kesh.json файлыг нээж хараарай.
Одоо интернэтээ САЛГААД дахин оролдоорой:
Хотын нэр (англиар): Tokyo
API-аас татаж байна...
Сүлжээний алдаа. Оролдлого 1/3
2 секунд хүлээж байна...
Сүлжээний алдаа. Оролдлого 2/3
4 секунд хүлээж байна...
Сүлжээний алдаа. Оролдлого 3/3
[Алдаа] 3 удаа оролдсон ч холбогдож чадсангүй.
Хотын нэр (англиар):Программ унасангүй. Гурван удаа оролдож, эвлэгээр бууж өглөө, дараагийн асуултаа асууж байна.
Бүлэг 11.5-ын БҮХ алдаа зохицуулалт ажиллаж байна.
Өргөтгөл 1: CSV экспорт (Бүлэг 6.3).
python
# tsag_agaar/export.py
import csv
from tsag_agaar.models import TsagAgaariinMedee
def csv_ruu_gargakh(medee: TsagAgaariinMedee, zam: str) -> None:
with open(zam, "w", encoding="utf-8-sig", newline="") as f:
bichigch = csv.DictWriter(
f, fieldnames=["ognoo", "bag", "ikh", "khur_tunadas"]
)
bichigch.writeheader()
for u in medee.udruud:
bichigch.writerow({
"ognoo": u.ognoo,
"bag": u.bag,
"ikh": u.ikh,
"khur_tunadas": u.khur_tunadas,
})encoding="utf-8-sig" — Excel-д кирилл зөв харагдана (Бүлэг 6.3).
Өргөтгөл 2: олон хот харьцуулах.
python
def khot_kharytsuulakh(neres: list[str]) -> None:
medeenuud = []
for ner in neres:
try:
medee = medee_olokh(ner)
if medee:
medeenuud.append(medee)
except ApiAlda:
continue
erembelsen = sorted(medeenuud, key=lambda m: m.odoo.temperatur, reverse=True)
for m in erembelsen:
print(f"{str(m.khot):<30} {m.odoo.temperatur:>6.1f}°C")sorted + lambda (Бүлэг 3.5).
Өргөтгөл 3: Session ашиглах (Бүлэг 11.5) — олон хот татахад хурдан болно.
1. Дугуй import
python
# api.py
from tsag_agaar.display import medee_kheveleкh # БУРУУ!api.py нь display.py -г МЭДЭХ ЁСГҮЙ. Хамаарлын чиглэл нь НЭГ ТАЛТАЙ.
2. main.py -д requests -ыг import хийх
python
# main.py
import requests # БУРУУ!main.py нь HTTP -ийн талаар мэдэх ёсгүй. api.py нь бүх сүлжээний алдааг ApiAlda болгож хувиргадаг.
3. config.py -г алгасаж, тоог кодод шууд бичих
python
hariult = requests.get(url, timeout=10) # 10 нь хаанаас?Бүх тогтмолыг config.py -д төвлөрүүл.
4. Кэшийн key -ыг нормчлохгүй
python
kesh[khotiin_ner] = ... # "Ulaanbaatar" ба "ulaanbaatar" ӨӨР key!.strip().lower() (Бүлэг 6.4).
5. main.py -д логик бичих
main.py нь ЗОХИЦУУЛАГЧ. Бүх жинхэнэ ажил нь package дотор.
Түвшин 4-ийн БҮХ мэдлэг нэг төсөлд нийллээ: decorator (Б10), HTTP/requests (Б11), package (Б12), dataclass (Б9), JSON (Б6), type hint (Б4), comprehension/zip (Б2).
ФАЙЛ БҮР НЭГ ЗОРИЛГОТОЙ:
config.py — бүх тогтмол НЭГ газарт
models.py — dataclass-ууд, from_api classmethod-ууд
decorators.py — цаг хэмжигч (домэйнээс хараат бус)
api.py — интернэттэй ярина
cache.py — файлд хадгална
display.py — харуулна
main.py — ЗОХИЦУУЛНА (богино!)
ХАМААРЛЫН ЧИГЛЭЛ НЭГ ТАЛТАЙ: main → display/api/cache → models/decorators → config. ЭСРЭГ чиглэлд ХЭЗЭЭ Ч (дугуй import).
Өөрийн алдааны class (ApiAlda) нь салгалтыг хамгаална. main.py нь requests -ыг МЭДЭХГҮЙ.
from_api classmethod — API dictionary → dataclass гүүр (Бүлэг 9.3).
asdict + Class(**d) — JSON гүүр. asdict нь гүнзгий, эсрэг зам нь ГАРААР.
@property — тооцоологдсон утгууд (dundaj_ikh, khurtai_udur) нь models.py -д амьдарна, display.py -д биш.
Кэш — ижил өгөгдлийг дахин татахгүй. Key -ыг НОРМЧЛО (.strip().lower()).
if __name__ == "__main__": — файл бүрийг ТУСАД НЬ турших. python -m tsag_agaar.api.
Retry + exponential backoff + 4xx/5xx ялгаа + timeout — Бүлэг 11.5-ын БҮХ алдаа зохицуулалт нэг функцэд (_khuselt).
ТҮВШИН 4 ДУУСЛАА. Таны программ одоо интернэттэй ярьж, олон файлаас бүрдэж, мэргэжлийн бүтэцтэй болов.
ТҮВШИН 5 — сүүлчийн түвшин — эхэлж байна.
Бүлэг 13: гүнзгий хэрэгслүүд. Generator ба yield. Өөрийн exception (ApiAlda -г та аль хэдийн ашигласан — одоо түүнийг зохих ёсоор нь ойлгоно). with яг юу хийдэг вэ. for loop-ийн ард юу байдаг вэ.
Бүлэг 14: async — товч танилцуулга. Уншиж таних түвшинд.
Бүлэг 15: CAPSTONE. Та FastAPI программ бичиж, ажиллуулж, browser-ээр нээж, /docs хуудсыг харна. Тэр мөч — таны бичсэн type hint-үүдээс автоматаар үүссэн интерактив баримт бичгийг харах мөч — бол энэ бүх курсийн ШАГНАЛ юм.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.