3-qism · Professional Python·15-bob
Type hint'lar, mypy va Pydantic
Tip annotatsiyalari nima beradi, asosiy va murakkab tiplar (Optional, Union, generics, Callable, TypedDict, Literal, Protocol), mypy bilan statik tekshiruv va Pydantic bilan runtime validatsiya — LLM'ning structured output'i uchun.
2-bobda ko'rdik: Python dinamik tiplangan, va narx_hisobla("3", 1500)
xato bermasdan "333...3" qaytaradi. Kichik skriptda bu muammo emas.
Lekin 50 ming qatorli kod bazasida, 10 kishilik jamoada, yoki AI agent
tizimida — bunday xatolar qimmatga tushadi. Type hint'lar bu
muammoni hal qiladi va zamonaviy professional Python'ning standart
qismiga aylangan: FastAPI, Pydantic, Anthropic va OpenAI SDK'lari,
PyTorch — hammasi type hint'larga tayanadi.
Type hint'lar, mypy bilan statik tekshiruv va Pydantic bilan runtime validatsiya farqi — aynan shu bobning asosiy g'oyasi.
1. Type hint'lar nima va nima emas#
def narx(soni: int, narxi: int) -> int:
return soni * narxiEng muhim fakt: Python type hint'larni ishga tushirishda tekshirmaydi.
narx("3", 1500) hamon ishlaydi. Hint'lar — bu metama'lumot:
uv add --dev mypy
uv run mypy src/main.py:7: error: Argument 1 to "narx" has incompatible type "str"; expected "int" [arg-type]2. Asosiy sintaksis#
# O'zgaruvchilar (odatda shart emas — tip xulosa qilinadi)
max_tokens: int = 1024
model_name: str = "claude-opus-5-5"
# Funksiyalar — eng muhim joy
def greet(name: str, excited: bool = False) -> str:
return f"Salom, {name}{'!' if excited else '.'}"
def log(msg: str) -> None: # hech narsa qaytarmaydi
print(msg)To'plamlar (Python 3.9+ — kichik harfli o'rnatilgan tiplar)#
scores: list[float] = [0.9, 0.85]
counts: dict[str, int] = {"a": 1}
unique: set[str] = {"x"}
point: tuple[float, float] = (1.0, 2.0) # aniq 2 element
shape: tuple[int, ...] = (32, 3, 224, 224) # istalgan uzunlik, hammasi int
matrix: list[list[float]] = [[1.0, 2.0]]Eski kodda from typing import List, Dict va List[int] ko'rasiz —
bu 3.8 va undan eskisi uchun edi; yangi kodda kichik harflardan
foydalaning.
None bo'lishi mumkin: X | None#
def find_user(user_id: int) -> User | None: # Python 3.10+
...
def find_user(user_id: int) -> Optional[User]: # eski yozuv, xuddi shu ma'no
...Endi mypy sizni None'ni tekshirishga majbur qiladi:
def get_count(d: dict[str, int], key: str) -> int:
v = d.get(key) # v: int | None
return v + 1 # ❌ mypy: Unsupported operand types for + ("None" and "int")def get_count(d: dict[str, int], key: str) -> int:
v = d.get(key)
if v is None:
return 0
return v + 1 # ✅ bu yerda mypy v ni int deb biladi ("type narrowing")Bu xususiyatning o'zi AttributeError: 'NoneType' object has no attribute...
xatolarining ko'pchiligini kod ishga tushmasidan oldin topadi.
Birlashma (Union)#
def to_text(value: str | bytes | int) -> str:
if isinstance(value, bytes):
return value.decode("utf-8")
return str(value)3. Abstrakt tiplar: aniq emas, imkoniyat so'rang#
from collections.abc import Iterable, Sequence, Mapping, Callable, Iterator
def total(values: Iterable[float]) -> float: # list, tuple, set, generator — hammasi bo'ladi
return sum(values)
def first(items: Sequence[str]) -> str: # indeks kerak: list, tuple, str
return items[0]
def render(params: Mapping[str, str]) -> str: # o'qish uchun lug'atga o'xshash narsa
return ", ".join(f"{k}={v}" for k, v in params.items())Qoida (Postel tamoyili): parametrlarda iloji boricha keng tip
qabul qiling (Iterable, Sequence, Mapping), qaytarishda — aniq
tip (list, dict). def total(values: list[float]) — tuple yoki
generator berilsa mypy shikoyat qiladi, garchi kod ishlasa ham.
| Kerakli imkoniyat | Tip |
|---|---|
Faqat aylanish (for) | Iterable[T] |
len() + indeks + aylanish | Sequence[T] |
| Kalit bo'yicha o'qish | Mapping[K, V] |
| O'zgartirish ham kerak | list[T], dict[K, V], MutableMapping |
| Generator qaytaradi | Iterator[T] (yoki Generator[Y, S, R]) |
Funksiya tiplari: Callable#
from collections.abc import Callable
def apply(fn: Callable[[str], int], items: list[str]) -> list[int]:
return [fn(x) for x in items]
apply(len, ["a", "bb"]) # ✅
Metric = Callable[[list[int], list[int]], float] # tip taxallusi4. Generics — tip parametrlari#
Funksiya har qanday tip bilan ishlasa, lekin kirish va chiqish tiplari bog'liq bo'lsa:
def first[T](items: Sequence[T]) -> T | None: # Python 3.12+ sintaksisi
return items[0] if items else None
x = first([1, 2, 3]) # mypy: x — int | None
y = first(["a", "b"]) # mypy: y — str | NoneEski (3.11 va oldingi) yozuv:
from typing import TypeVar
T = TypeVar("T")
def first(items: Sequence[T]) -> T | None: ...Generik klass:
class Cache[K, V]:
def __init__(self) -> None:
self._data: dict[K, V] = {}
def get(self, key: K) -> V | None:
return self._data.get(key)
def set(self, key: K, value: V) -> None:
self._data[key] = value
emb_cache: Cache[str, list[float]] = Cache()Tip taxalluslari#
type Vector = list[float] # Python 3.12+
type Messages = list[dict[str, str]]
def cosine(a: Vector, b: Vector) -> float: ...5. Ma'lumot shakllari: TypedDict va Literal#
API'lar bilan ishlaganda lug'atlar ko'p. TypedDict lug'atning
shaklini tasvirlaydi:
from typing import Literal, NotRequired, TypedDict
class Message(TypedDict):
role: Literal["user", "assistant"] # faqat shu ikki qiymat
content: str
name: NotRequired[str] # ixtiyoriy kalit
msg: Message = {"role": "user", "content": "Salom"} # ✅
bad: Message = {"role": "system", "content": "..."} # ❌ mypy: "system" ruxsat etilmaganLiteral — "faqat shu aniq qiymatlar": Literal["train", "val", "test"],
Literal[0, 1]. Kichik to'plam uchun Enumga (13-bob) yengil alternativa.
6. Protocol — strukturaviy tiplar#
13-bobda ko'rdik. Type hint'lar kontekstida — "shu metodlari bor har qanday narsa":
from typing import Protocol
class SupportsComplete(Protocol):
def complete(self, prompt: str, *, max_tokens: int = ...) -> str: ...
def summarize(llm: SupportsComplete, text: str) -> str:
return llm.complete(f"Qisqacha xulosa:\n{text}", max_tokens=200)Endi summarizega Claude klienti, mahalliy model, yoki testdagi soxta
obyekt — complete metodi bo'lgan har qanday narsa berilishi mumkin.
7. Boshqa foydali vositalar#
from typing import Any, Final, Self, cast, override, TYPE_CHECKING
MAX_RETRIES: Final = 5 # qayta tayinlash — mypy xatosi
def load(raw: Any) -> dict: ... # Any — "tekshirma" (qochish yo'li; kam ishlating)
class Builder:
def set_model(self, m: str) -> Self: # voris klasslarda ham to'g'ri tip
self.model = m
return self
class MyModel(BaseModel):
@override # ota klassda bunday metod bo'lmasa — mypy xatosi
def predict(self, x): ...
if TYPE_CHECKING: # faqat tekshiruv uchun import (aylanma importdan qochish)
from .models import ClassifierAny va cast — mypy'ni "o'chirish" vositalari. Ular ba'zan kerak
(masalan, xom JSON bilan ishlashda), lekin har biri — tip xavfsizligida
teshik. Tashqi ma'lumot uchun Any o'rniga Pydantic ishlating (pastda).
8. mypy'ni loyihada sozlash#
# pyproject.toml
[tool.mypy]
python_version = "3.12"
strict = true # eng qattiq rejim — yangi loyihalar uchun tavsiya
warn_unused_ignores = true
plugins = ["pydantic.mypy"]
[[tool.mypy.overrides]]
module = ["some_untyped_lib.*"]
ignore_missing_imports = trueMavjud (tiplanmagan) loyihaga qo'shishda strictni birdaniga yoqmang —
avval oddiy rejim, keyin modulma-modul qattiqlashtiring. Ba'zan bitta
qatorni o'chirish kerak bo'ladi: # type: ignore[arg-type] — har doim
xato kodini ko'rsating va nima uchunligini izohlang.
9. Pydantic — runtime validatsiya#
Type hint'lar sizning kodingizni tekshiradi. Lekin tashqaridan keladigan ma'lumot — API so'rovi, konfiguratsiya fayli, LLM javobi — statik tekshiruvdan o'tmaydi. U yerda Pydantic kerak: u type hint'larni o'qib, ma'lumotni ishga tushirish paytida tekshiradi va aylantiradi.
uv add pydanticfrom typing import Literal
from pydantic import BaseModel, Field, ValidationError
class Review(BaseModel):
text: str = Field(min_length=1)
rating: int = Field(ge=1, le=5) # 1 ≤ rating ≤ 5
sentiment: Literal["positive", "negative", "neutral"]
tags: list[str] = [] # Pydantic'da bu xavfsiz — har obyektga nusxa
r = Review.model_validate({"text": "Zo'r", "rating": "5", "sentiment": "positive"})
r.rating, type(r.rating) # (5, <class 'int'>) — "5" satri int'ga aylantirildiNoto'g'ri ma'lumot:
try:
Review(text="", rating=7, sentiment="good")
except ValidationError as e:
print(e)3 validation errors for Review
text
String should have at least 1 character [type=string_too_short, input_value='', input_type=str]
rating
Input should be less than or equal to 5 [type=less_than_equal, input_value=7, input_type=int]
sentiment
Input should be 'positive', 'negative' or 'neutral' [type=literal_error, input_value='good', input_type=str]Barcha xatolar birdaniga, aniq joyi va sababi bilan. e.errors() —
xuddi shu ma'lumot lug'atlar ro'yxati sifatida (API javobiga qaytarish
uchun).
Asosiy amallar#
Review.model_validate(data_dict) # lug'atdan
Review.model_validate_json(json_string) # JSON satrdan (tezroq — parse + validate birga)
r.model_dump() # → dict
r.model_dump_json() # → JSON satr
r.model_copy(update={"rating": 4}) # o'zgartirilgan nusxa
Review.model_json_schema() # → JSON Schema (LLM'ga berish uchun!)Maxsus tekshiruvlar#
from pydantic import BaseModel, field_validator, model_validator
class TrainingExample(BaseModel):
prompt: str
completion: str
language: str = "uz"
@field_validator("prompt", "completion")
@classmethod
def strip_and_check(cls, v: str) -> str:
v = v.strip()
if not v:
raise ValueError("bo'sh bo'lmasligi kerak")
return v # o'zgartirilgan qiymat saqlanadi
@model_validator(mode="after")
def different(self) -> "TrainingExample":
if self.prompt == self.completion:
raise ValueError("prompt va completion bir xil")
return selfQat'iy rejim#
Standart holatda Pydantic "aqlli" aylantiradi ("5" → 5). Ba'zan bu
istalmaydi:
class Strict(BaseModel):
model_config = {"strict": True}
n: int
Strict(n="5") # ValidationError: int_typeSozlamalar uchun: pydantic-settings#
from pydantic_settings import BaseSettings, SettingsConfigDict # uv add pydantic-settings
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env")
anthropic_api_key: str # majburiy — yo'q bo'lsa, ishga tushishda aniq xato
model: str = "claude-opus-5-5"
max_tokens: int = 1024 # ".env"dagi "2048" satri int'ga aylanadi
debug: bool = False # "1", "true", "yes" → True
settings = Settings() # muhit o'zgaruvchilari + .env dan o'qiydi9-bob mashqidagi config.py — bitta klassga aylandi.
10. Pydantic va LLM: structured output#
LLM bilan ishlashning eng muhim naqshlaridan biri: modeldan aniq sxemadagi JSON olish. Pydantic bu yerda ikki vazifani bajaradi:
- Sxema beradi —
model_json_schema()modelga "javobing shunday shaklda bo'lsin" deb aytish uchun; - Javobni tekshiradi — model nima qaytarsa ham, kodingizga faqat to'g'ri tiplangan obyekt yetib boradi.
from typing import Literal
from pydantic import BaseModel, Field
class Entity(BaseModel):
name: str
type: Literal["person", "organization", "location"]
class Extraction(BaseModel):
summary: str = Field(description="1-2 gaplik xulosa")
sentiment: Literal["positive", "negative", "neutral"]
entities: list[Entity]
schema = Extraction.model_json_schema()
# → bu sxema API'ga beriladi (23-bobda Anthropic SDK'ning structured outputs imkoniyati)
raw = '{"summary": "Kompaniya yangi ofis ochdi.", "sentiment": "positive", "entities": [{"name": "Toshkent", "type": "location"}]}'
result = Extraction.model_validate_json(raw)
result.entities[0].name # 'Toshkent' — muharrir avtomatik to'ldiradi, mypy tekshiradiAnthropic SDK'sida client.messages.parse(..., output_format=Extraction)
kabi yordamchilar aynan shu mexanizmdan foydalanadi: Pydantic modelini
sxemaga aylantiradi, modelni sxemaga rioya qilishga majburlaydi va javobni
qaytadan Pydantic obyektiga aylantiradi. 23-bobda to'liq misol.
11. Qayerda qanchalik tiplash kerak#
| Kod turi | Tavsiya |
|---|---|
| Jupyter tajribalari | Shart emas; muhim funksiyalarga yozing |
| Bir martalik skript | Funksiya imzolariga yozing |
| Kutubxona / paket | To'liq, mypy --strict |
| Servis (FastAPI, agent) | To'liq + tashqi ma'lumot uchun Pydantic |
| Tashqi ma'lumot chegarasi (API, fayl, LLM javobi) | Pydantic — majburiy |
Nima noto'g'ri ketishi mumkin#
| Xato | Oqibat | Tuzatish |
|---|---|---|
| Hint'lar runtime'da tekshiriladi deb o'ylash | Noto'g'ri tip jim o'tadi | mypy/pyright CI'da; tashqi ma'lumot uchun Pydantic |
def f(x: list = []) | Tip to'g'ri, lekin mutable default (8-bob) | x: list[int] | None = None |
-> None deb yozib qiymat qaytarish | mypy xatosi | To'g'ri qaytish tipi |
Optional[X] = "ixtiyoriy parametr" deb o'ylash | Optional faqat "None bo'lishi mumkin" degani | Default qiymat — alohida narsa |
Hamma joyda Any | Tekshiruv ma'nosiz | Aniq tiplar, object, Protocol, Pydantic |
list[float] parametr — tuple berildi | mypy shikoyat qiladi | Sequence[float] yoki Iterable[float] |
LLM javobini json.loads va lug'at sifatida ishlatish | Kalit yo'q / noto'g'ri tip — keyinroq xato | Model.model_validate_json(...) |
# type: ignore sababsiz | Haqiqiy xatolar yashirinadi | Xato kodini ko'rsating, izoh yozing |
1. Tiplang va mypy'dan o'tkazing
Oldingi boblardagi uchta funksiyangizni (normalize, batched,
build_prompt) to'liq tiplang va uv run mypy --strict dan xatosiz
o'tkazing. Ataylab noto'g'ri chaqiruv yozib, mypy uni topishini tekshiring.
Maslahatni ko'rsatish
Avval mypy'ni --strict bilan ishga tushiring, xabarlarni birma-bir tuzating. Iterable/Sequence tanlovi, None narrowing.
2. Generik funksiya
group_by(items, key) generik funksiyasini yozing: key funksiyasi
bo'yicha guruhlab lug'at qaytarsin. mypy group_by(["olma", "anor"], len)
natijasini dict[int, list[str]] deb bilishini reveal_type bilan
tekshiring.
Maslahatni ko'rsatish
def group_by[T, K](items: Iterable[T], key: Callable[[T], K]) -> dict[K, list[T]]
3. Dataset validatsiyasi
TrainingExample modeli bilan JSONL faylni tekshiruvchi skript yozing:
yaroqli qatorlarni yangi faylga yozsin, yaroqsizlari uchun hisobot
chiqarsin — qaysi maydon qancha marta xato bergan. 11-bobdagi dataset
skriptiga ulang.
Maslahatni ko'rsatish
Har qator: try: TrainingExample.model_validate_json(line) except ValidationError as e: xatolarni yig'ing. Hisobot: Counter(err['loc'][0] for err in ...).
4. LLM javob sxemasi
"Ish e'lonidan ma'lumot ajratish" uchun Pydantic sxemasini loyihalang:
lavozim, kompaniya, shahar, maosh oralig'i (ixtiyoriy, min ≤ max
tekshiruvi bilan), talab qilinadigan ko'nikmalar ro'yxati, masofaviy ish
(bool). model_json_schema()ni chiqaring va qo'lda yozilgan 3 xil "model
javobi" (biri buzilgan) bilan validatsiyani sinab ko'ring.
- 01Type hint'lar runtime'da tekshirilmaydi — ularni muharrir, mypy/pyright va Pydantic ishlatadi.
- 02list[int], dict[str, float], tuple[int, ...], X | None — zamonaviy sintaksis.
- 03X | None — mypy None'ni tekshirishga majbur qiladi; bu NoneType xatolarining ko'pini oldini oladi.
- 04Parametrda keng (Iterable, Sequence, Mapping), qaytishda aniq tip.
- 05Generics: def f[T](x: Sequence[T]) -> T; TypedDict va Literal — lug'at shakli va aniq qiymatlar.
- 06Protocol — almashtiriladigan komponentlar uchun strukturaviy tiplar.
- 07Pydantic — tashqi ma'lumot chegarasida runtime validatsiya va aylantirish; ValidationError barcha xatolarni beradi.
- 08LLM structured output: Pydantic → JSON Schema → model → model_validate_json.
Mustahkamlash
Test va amaliy masalalar
Avval testni ishlang — har javobdan keyin izoh chiqadi. Keyin masalalarni shu yerning o'zida yechib, brauzerdagi haqiqiy Python bilan avtomatik tekshiring. Natijalaringiz kurs progressi sahifasida jamlanadi.
Bilimni tekshirish
Bob bo'yicha test
12 ta savol · har javobdan keyin to'g'ri yoki xatoligi va izoh ko'rsatiladi · oxirida natija foizda.
Amaliy masalalar
Kodni yozing va «Tekshirish»ni bosing (yoki Ctrl/⌘ + Enter). Kodingiz shu brauzerda saqlanib turadi.
01Generik group_by
group_by(items, key) — key(x) bo'yicha guruhlab dict[K, list[T]] qaytarsin (tartib saqlansin). Type hint'lar bilan yozing (def group_by[T, K](items: Iterable[T], key: Callable[[T], K]) -> dict[K, list[T]]).
02Pydantic model
Review(BaseModel): text: str (kamida 1 belgi, chetlari tozalanadi), rating: int (1..5), sentiment: Literal["positive", "negative", "neutral"], tags: list[str] = []. parse_reviews(rows) — yaroqlilarini Review ro'yxati, yaroqsizlari sonini qaytarsin: (reviews, n_bad).
03LLM javobidan ma'lumot
Extraction(BaseModel): summary: str, entities: list[Entity], bu yerda Entity(BaseModel): name: str, type: Literal["person", "organization", "location"]. extract(raw_json) — JSON satrni tekshirib Extraction qaytarsin; yaroqsiz bo'lsa None.