IlmHamroh
Python kursi/Turlar tizimi1/9-dars22 daqiqa
Mundarija (21)

13.1-dars: Dinamik va statik turlash

13-QISM — TURLAR TIZIMI · 1-dars


1. Kirish va motivatsiya

Python — dinamik turlangan til: o'zgaruvchi turi oldindan e'lon qilinmaydi va ish vaqtida o'zgarishi mumkin. Bu tezkor yozishga imkon beradi, lekin xatolar ham ish vaqtigacha yashirinib turadi.

Real vaziyat. Xabarnoma servisida funksiya oluvchilar ro'yxatini kutardi:

python
def yuborish(oluvchilar, matn):
    for manzil in oluvchilar:
        jonat(manzil, matn)

Yangi endpoint unga bitta manzilni satr sifatida uzatdi: yuborish("aziz@wisar.uz", matn). Xato chiqmadi — satr ham iteratsiyalanadi, shuning uchun servis "a", "z", "i", "z" ... manzillariga 16 ta buzuq xabar yubordi. Muammo faqat kechqurun, yetkazib berish xatolari hisobotida ko'rindi.

Bitta annotatsiya buni yozish paytidayoq ko'rsatardi:

python
def yuborish(oluvchilar: list[str], matn: str) -> int: ...

Python annotatsiyalarni ish vaqtida tekshirmaydi — ular tashqi vositalar (mypy, pyright) va odamlar uchun. Shuning uchun "turlar bor" degani "xato bo'lmaydi" degani emas: turlar tizimi faqat siz uni ishlatganda foyda beradi.

Bu darsda:

  • Dinamik, statik va bosqichma-bosqich (gradual) turlash
  • Annotatsiyalar nima va nima emas
  • Annotatsiyalar — ma'lumot: __annotations__, annotationlib 3.14-bob, get_type_hints
  • Kechiktirilgan hisoblash (PEP 649) va oldinga havolalar
  • Duck typing va nominal turlash
  • Ish vaqtidagi tekshiruv: qachon foydali, qachon ortiqcha
  • Amaliy: annotatsiya qamrovini o'lchaydigan vosita

2. Nazariya — chuqur tushuntirish

2.1. Turlash modellari

Model Ma'nosi Misollar
Dinamik Tur qiymatga tegishli, ish vaqtida tekshiriladi Python, Ruby, JavaScript
Statik Tur o'zgaruvchiga tegishli, kompilyatsiyada tekshiriladi Java, Go, Rust, C#
Kuchli (strong) Turlar avtomatik aralashmaydi: 1 + "2" → xato Python
Kuchsiz (weak) Avtomatik o'girish: 1 + "2" → "12" JavaScript, PHP

Python — dinamik va kuchli: turlar ish vaqtida tekshiriladi, lekin o'z-o'zidan o'girilmaydi.

Bosqichma-bosqich turlash (gradual typing) — Python tanlagan uchinchi yo'l: annotatsiyalar ixtiyoriy; annotatsiyalangan qismlarni tashqi tekshiruvchi tekshiradi, qolgani dinamik qoladi.

2.2. Annotatsiyalar nima emas

python
def yosh_qosh(yosh: int) -> int:
    return yosh + 1

yosh_qosh("yigirma")     # TypeError — lekin annotatsiya tufayli emas!
Annotatsiya nima emas Tafsilot
Ish vaqti tekshiruvi emas Interpretator turlarni tekshirmaydi
Majburlash emas Noto'g'ri tur uzatish mumkin
Tezlashtirish emas CPython ulardan optimallashtirish uchun foydalanmaydi (12.8-dars)
Hujjat o'rnini bosuvchi emas Ma'noni izohlash baribir kerak
Annotatsiya nima Tafsilot
Statik tekshiruvchi uchun ma'lumot mypy, pyright, ty, pyrefly
IDE uchun: avtoto'ldirish, refaktoring Eng kunlik foyda
Hujjat Imzoni o'qish osonlashadi
Ish vaqtidagi kutubxonalar uchun manba pydantic, dataclasses, fastapi (13.8-dars)

2.3. Annotatsiyalar — oddiy ma'lumot

python
def komissiya(summa: float, foiz: float = 2.0) -> float: ...

komissiya.__annotations__     # {'summa': float, 'foiz': float, 'return': float}
Joy Qayerda saqlanadi
Funksiya funksiya.__annotations__
Klass maydonlari Klass.__annotations__
Modul o'zgaruvchilari modul.__annotations__
Lokal o'zgaruvchi (x: int = 1) Hech qayerda — faqat kompilyator uchun

3.14 (PEP 649, 749): annotatsiyalar kechiktirilgan hisoblanadi — funksiya __annotate__ orqali ular so'ralganda quriladi. Natijada import tezroq va oldinga havolalar (-> "Tugun") tabiiy ishlaydi.

python
import annotationlib
annotationlib.get_annotations(f)                                  # qiymatlar
annotationlib.get_annotations(f, format=annotationlib.Format.STRING)      # satrlar
annotationlib.get_annotations(f, format=annotationlib.Format.FORWARDREF)  # hal qilinmaganlari ForwardRef
typing.get_type_hints(f)                                          # satrlarni hal qiladi

from __future__ import annotations — 3.14 gacha ishlatilgan yechim; yangi kodda kerak emas.

2.4. Duck typing va nominal turlash

python
def yoz(oqim):
    oqim.write("salom")      # "write metodi bo'lsa — yetarli"
Yondashuv Savol Python'da
Nominal "Bu klass Oqim dan merosmi?" isinstance, klasslar
Strukturaviy (duck) "Bu obyektda write bormi?" Protokollar (13.5-dars)

Bosqichma-bosqich turlash ikkalasini ham qo'llab-quvvatlaydi: nominal uchun klasslar, strukturaviy uchun Protocol.

2.5. Statik tekshiruvchilar

Vosita Xususiyati
mypy Etalon, PEP lar muallifi jamoasi, sekinroq
pyright (Pylance) TypeScript'da, juda tez, VS Code'da sukut
ty, pyrefly Rust'da yozilgan yangi avlod (2025), juda tez, rivojlanmoqda
pytype Xulosa (inference) ga ko'proq tayanadi

Ular kodni bajarmasdan tahlil qiladi (12.1-dars: AST). Qat'iylik darajasi sozlanadi: strict, disallow_untyped_defs va h.k. (13.7-dars).

2.6. Qayerda annotatsiya eng foydali

Joy Foyda
Ommaviy API (kutubxona, servis chegarasi) Eng katta — ishlatuvchilar uchun
Murakkab ma'lumot tuzilmalari dict[str, list[tuple[int, str]]] — o'qishni osonlashtiradi
Uzoq yashaydigan kod Refaktoring xavfsizroq
Qisqa skript, tajriba Kam — ixtiyoriy
Testlar Odatda kerak emas (lekin fikstura imzolari foydali)

Amaliy strategiya: chegaralardan boshlang — tashqi ma'lumot kirib keladigan funksiyalarni annotatsiyalang, ichkariga asta-sekin tarqating.

2.7. Ish vaqtidagi tekshiruv

Annotatsiyalarga qarab ish vaqtida tekshirish mumkin (isinstance, dekorator, pydantic), lekin:

Foyda Narx
Tashqi ma'lumotni ishonchli qilish (JSON, forma, API) Har chaqiruvda tekshiruv vaqti
Xatoni manbaga yaqin joyda ushlash list[int] kabi umumiy turlarni to'liq tekshirish qimmat

Qoida: chegarada ish vaqtida validatsiya (13.8-dars), ichkarida statik tekshiruv.


3. Tez ma'lumotnoma

Annotatsiya sintaksisi

python
def f(a: int, b: str = "x", *sonlar: float, **qolgan: object) -> bool: ...
maydon: dict[str, int] = {}
NOMLAR: list[str] = []

Ma'lumot sifatida

f.__annotations__            Klass.__annotations__       modul.__annotations__
typing.get_type_hints(f)     satrlarni hal qiladi
annotationlib.get_annotations(f, format=Format.STRING | FORWARDREF | VALUE)   # 3.14
inspect.signature(f)

Qoidalar

ish vaqtida tekshirilmaydi        →  mypy / pyright
chegarada validatsiya             →  pydantic (13.8)
oldinga havola                    →  "Tugun" yoki 3.14 da to'g'ridan-to'g'ri
faqat tekshiruvchi uchun import   →  if TYPE_CHECKING:

4. Batafsil misollar

Misol 1 — Ish vaqti annotatsiyalarni tekshirmaydi

python
"""Satrni ro'yxat o'rniga uzatish: jim xato; xato qayerda ko'rinadi; kuchli turlash; duck typing."""


def yuborish(oluvchilar: list[str], matn: str) -> int:
    """Har bir manzilga xabar yuboradi va yuborilganlar sonini qaytaradi."""
    yuborildi = 0
    for manzil in oluvchilar:
        if "@" not in manzil:
            navbat_xato.append(manzil)
        else:
            navbat_ok.append(manzil)
        yuborildi += 1
    return yuborildi


navbat_ok: list[str] = []
navbat_xato: list[str] = []

print("=== 1. Annotatsiya majburlamaydi ===")
print(f"  to'g'ri chaqiruv: {yuborish(['aziz@wisar.uz', 'dilnoza@wisar.uz'], 'salom')} ta xabar")
print(f"  navbatlar: ok={len(navbat_ok)}, xato={len(navbat_xato)}")

navbat_ok.clear()
navbat_xato.clear()
soni = yuborish("aziz@wisar.uz", "salom")          # ❌ list emas, str uzatildi
print(f"  yuborish('aziz@wisar.uz', ...) xato bermadi: {soni} ta 'xabar'")
print(f"  navbatlar: ok={len(navbat_ok)}, xato={len(navbat_xato)} — har harf alohida 'manzil' bo'ldi")
print(f"  birinchi uchtasi: {navbat_xato[:3]}")
print("  ⚠️ satr ham iteratsiyalanadi — shuning uchun xato jim o'tdi")

print("\n=== 2. Xato ancha keyin ko'rinadi ===")


def hisobot(manzillar: list[str]) -> str:
    domenlar = {m.split("@")[1] for m in manzillar}
    return f"{len(manzillar)} ta xabar, domenlar: {sorted(domenlar)}"


try:
    print(hisobot(navbat_xato))
except IndexError as xato:
    print(f"  IndexError: {xato}")
print("  ⭐ xato manbai — yuborish() chaqiruvi, lekin u hisobot bosqichida chiqdi")

print("\n=== 3. Python kuchli turlangan ===")
for ifoda, amal in [("1 + '2'", lambda: 1 + "2"), ("'2' * 3", lambda: "2" * 3), ("[1] + (2,)", lambda: [1] + (2,))]:
    try:
        print(f"  {ifoda:12} → {amal()!r}")
    except TypeError as xato:
        print(f"  {ifoda:12} → TypeError: {xato}")

print("\n=== 4. Duck typing ===")


class Fayl:
    def __init__(self):
        self.yozilgan = []

    def write(self, matn):
        self.yozilgan.append(matn)


class Tarmoq:
    def __init__(self):
        self.paketlar = []

    def write(self, matn):
        self.paketlar.append(matn.encode())


def jurnalga_yoz(oqim, xabar):
    """Tur emas, xulq muhim: write() bo'lsa yetarli."""
    oqim.write(xabar)


f, tarmoq = Fayl(), Tarmoq()
jurnalga_yoz(f, "boshlandi")
jurnalga_yoz(tarmoq, "boshlandi")
print(f"  ikki xil klass, bitta funksiya: {f.yozilgan}, {tarmoq.paketlar}")


class Sanoq:
    pass


try:
    jurnalga_yoz(Sanoq(), "salom")
except AttributeError as xato:
    print(f"  write yo'q bo'lsa: AttributeError: {xato}")

Natijaning muhim qismi:

text
=== 1. Annotatsiya majburlamaydi ===
  to'g'ri chaqiruv: 2 ta xabar
  navbatlar: ok=2, xato=0
  yuborish('aziz@wisar.uz', ...) xato bermadi: 13 ta 'xabar'
  navbatlar: ok=1, xato=12 — har harf alohida 'manzil' bo'ldi
  birinchi uchtasi: ['a', 'z', 'i']
  ⚠️ satr ham iteratsiyalanadi — shuning uchun xato jim o'tdi

=== 2. Xato ancha keyin ko'rinadi ===
  IndexError: list index out of range
  ⭐ xato manbai — yuborish() chaqiruvi, lekin u hisobot bosqichida chiqdi

=== 3. Python kuchli turlangan ===
  1 + '2'      → TypeError: unsupported operand type(s) for +: 'int' and 'str'
  '2' * 3      → '222'
  [1] + (2,)   → TypeError: can only concatenate list (not "tuple") to list

=== 4. Duck typing ===
  ikki xil klass, bitta funksiya: ['boshlandi'], [b'boshlandi']
  write yo'q bo'lsa: AttributeError: 'Sanoq' object has no attribute 'write'

Nima ko'rsatdi: 2.1, 2.2, 2.4-bo'limlar — annotatsiya bor edi, lekin uni hech kim tekshirmadi; xato esa manbadan uzoqda chiqdi.

Misol 2 — Annotatsiyalar — oddiy ma'lumot

python
"""__annotations__; lokal o'zgaruvchi annotatsiyasi saqlanmaydi; oldinga havolalar; annotationlib formatlari 3.14-bob; get_type_hints; signature."""

import annotationlib
import inspect
import typing


def buyurtma(mijoz: str, soni: int = 1, *qoshimcha: float, yetkazish: "Manzil | None" = None) -> "Chek":
    jami: float = soni * 12_000.0        # lokal annotatsiya
    return jami


class Manzil:
    shahar: str
    kocha: str = "noma'lum"


class Chek:
    raqam: int
    manzil: "Manzil"


print("=== 1. Funksiya annotatsiyalari ===")
for nom, qiymat in buyurtma.__annotations__.items():
    print(f"  {nom:12} {qiymat!r}")
print(f"  lokal 'jami' annotatsiyasi saqlanganmi: {'jami' in buyurtma.__annotations__}")

print("\n=== 2. Klass va modul ===")
print(f"  Manzil.__annotations__: {Manzil.__annotations__}")
print(f"  sukut qiymat alohida: Manzil.kocha = {Manzil.kocha!r}, 'shahar' atributi bormi: {hasattr(Manzil, 'shahar')}")
print(f"  modul darajasidagi annotatsiyalar mavjud: {hasattr(__import__('sys').modules['__main__'], '__annotations__')}")

print("\n=== 3. 3.14: kechiktirilgan hisoblash ===")
print(f"  __annotate__ funksiyasi bor: {hasattr(buyurtma, '__annotate__')}")
satrlar = annotationlib.get_annotations(buyurtma, format=annotationlib.Format.STRING)
print(f"  STRING format: {satrlar}")
qiymatlar = annotationlib.get_annotations(buyurtma)
print(f"  VALUE format: mijoz={qiymatlar['mijoz'].__name__}, return={qiymatlar['return']!r}")

print("\n=== 4. Oldinga havolalar ===")
print(f"  Chek.__annotations__: {Chek.__annotations__}")
hal_qilingan = typing.get_type_hints(Chek)
print(f"  get_type_hints hal qildi: {{'raqam': {hal_qilingan['raqam'].__name__}, 'manzil': {hal_qilingan['manzil'].__name__}}}")


def noaniq(x: "HaliYoq") -> None:
    return None


try:
    typing.get_type_hints(noaniq)
except NameError as xato:
    print(f"  mavjud bo'lmagan turga havola: NameError: {xato}")
fr = annotationlib.get_annotations(noaniq, format=annotationlib.Format.FORWARDREF)
print(f"  FORWARDREF format xato bermaydi: {fr}")

print("\n=== 5. inspect.signature ===")
imzo = inspect.signature(buyurtma)
print(f"  imzo: {imzo}")
for nom, p in imzo.parameters.items():
    print(f"  {nom:12} turi: {p.annotation!r}, sukut: {p.default!r}, ko'rinishi: {p.kind.name}")

Natijaning muhim qismi:

text
=== 1. Funksiya annotatsiyalari ===
  mijoz        <class 'str'>
  soni         <class 'int'>
  qoshimcha    <class 'float'>
  yetkazish    'Manzil | None'
  return       'Chek'
  lokal 'jami' annotatsiyasi saqlanganmi: False

=== 2. Klass va modul ===
  Manzil.__annotations__: {'shahar': <class 'str'>, 'kocha': <class 'str'>}
  sukut qiymat alohida: Manzil.kocha = "noma'lum", 'shahar' atributi bormi: False
  modul darajasidagi annotatsiyalar mavjud: True

=== 3. 3.14: kechiktirilgan hisoblash ===
  __annotate__ funksiyasi bor: True
  STRING format: {'mijoz': 'str', 'soni': 'int', 'qoshimcha': 'float', 'yetkazish': 'Manzil | None', 'return': 'Chek'}
  VALUE format: mijoz=str, return='Chek'

=== 4. Oldinga havolalar ===
  Chek.__annotations__: {'raqam': <class 'int'>, 'manzil': 'Manzil'}
  get_type_hints hal qildi: {'raqam': int, 'manzil': Manzil}
  mavjud bo'lmagan turga havola: NameError: name 'HaliYoq' is not defined
  FORWARDREF format xato bermaydi: {'x': 'HaliYoq', 'return': None}

=== 5. inspect.signature ===
  imzo: (mijoz: str, soni: int = 1, *qoshimcha: float, yetkazish: 'Manzil | None' = None) -> 'Chek'
  mijoz        turi: <class 'str'>, sukut: <class 'inspect._empty'>, ko'rinishi: POSITIONAL_OR_KEYWORD
  soni         turi: <class 'int'>, sukut: 1, ko'rinishi: POSITIONAL_OR_KEYWORD
  qoshimcha    turi: <class 'float'>, sukut: <class 'inspect._empty'>, ko'rinishi: VAR_POSITIONAL
  yetkazish    turi: 'Manzil | None', sukut: None, ko'rinishi: KEYWORD_ONLY

Nima ko'rsatdi: 2.3-bo'lim.

Misol 3 — Ish vaqtida tekshirish: qachon arzon, qachon qimmat

python
"""Annotatsiyaga qarab tekshiradigan dekorator; oddiy va umumiy turlar; narxi; chegarada tekshirish g'oyasi."""

import functools
import time
import typing


def turlarni_tekshir(funksiya):
    """Oddiy tekshiruvchi: faqat isinstance bilan tekshirib bo'ladigan turlar."""
    izohlar = typing.get_type_hints(funksiya)

    @functools.wraps(funksiya)
    def orov(*args, **kwargs):
        imzo = list(funksiya.__code__.co_varnames[:funksiya.__code__.co_argcount])
        for nom, qiymat in list(zip(imzo, args)) + list(kwargs.items()):
            kutilgan = izohlar.get(nom)
            if isinstance(kutilgan, type) and not isinstance(qiymat, kutilgan):
                raise TypeError(f"{funksiya.__name__}(): {nom} uchun {kutilgan.__name__} kutildi, {type(qiymat).__name__} keldi")
        natija = funksiya(*args, **kwargs)
        qaytish = izohlar.get("return")
        if isinstance(qaytish, type) and not isinstance(natija, qaytish):
            raise TypeError(f"{funksiya.__name__}(): {qaytish.__name__} qaytishi kerak edi, {type(natija).__name__} qaytdi")
        return natija
    return orov


@turlarni_tekshir
def chegirma(narx: float, foiz: int) -> float:
    return narx * (100 - foiz) / 100


print("=== 1. Chegarada ushlash ===")
print(f"  to'g'ri: chegirma(10_000.0, 15) = {chegirma(10_000.0, 15)}")
for args in [("10000", 15), (10_000.0, "15")]:
    try:
        chegirma(*args)
    except TypeError as xato:
        print(f"  {args} → TypeError: {xato}")

print("\n=== 2. Umumiy turlarni isinstance tekshira olmaydi ===")


def ballar(qiymatlar: list[int]) -> float:
    return sum(qiymatlar) / len(qiymatlar)


izohlar = typing.get_type_hints(ballar)
print(f"  annotatsiya: {izohlar['qiymatlar']}")
print(f"  isinstance([1, 2], list[int]) ni to'g'ridan-to'g'ri ishlatib bo'ladimi:", end=" ")
try:
    isinstance([1, 2], izohlar["qiymatlar"])
    print("ha")
except TypeError as xato:
    print(f"yo'q — TypeError: {xato}")
print(f"  faqat asosini tekshirish mumkin: isinstance([1, 2], {typing.get_origin(izohlar['qiymatlar']).__name__}) = "
      f"{isinstance([1, 2], typing.get_origin(izohlar['qiymatlar']))}")
print(f"  elementlarni tekshirish — har element uchun sikl: {all(isinstance(x, int) for x in [1, 2, 3])}")

print("\n=== 3. Narxi ===")


def toza(narx: float, foiz: int) -> float:
    return narx * (100 - foiz) / 100


tekshiruvli = turlarni_tekshir(toza)


def olch(f, takror=200_000, urinish=3):
    eng = float("inf")
    for _ in range(urinish):
        bosh = time.perf_counter()
        for _ in range(takror):
            f(10_000.0, 15)
        eng = min(eng, time.perf_counter() - bosh)
    return eng


t_toza, t_tekshiruvli = olch(toza), olch(tekshiruvli)
print(f"  natijalar bir xil: {toza(10_000.0, 15) == tekshiruvli(10_000.0, 15)}")
print(f"  tekshiruvli variant kamida 2 barobar sekin: {t_tekshiruvli / t_toza >= 2}")
print("  ⭐ shuning uchun: chegarada (API, fayl, forma) tekshiring, ichki issiq funksiyalarda emas")

Natijaning muhim qismi:

text
=== 1. Chegarada ushlash ===
  to'g'ri: chegirma(10_000.0, 15) = 8500.0
  ('10000', 15) → TypeError: chegirma(): narx uchun float kutildi, str keldi
  (10000.0, '15') → TypeError: chegirma(): foiz uchun int kutildi, str keldi

=== 2. Umumiy turlarni isinstance tekshira olmaydi ===
  annotatsiya: list[int]
  isinstance([1, 2], list[int]) ni to'g'ridan-to'g'ri ishlatib bo'ladimi: yo'q — TypeError: isinstance() argument 2 cannot be a parameterized generic
  faqat asosini tekshirish mumkin: isinstance([1, 2], list) = True
  elementlarni tekshirish — har element uchun sikl: True

=== 3. Narxi ===
  natijalar bir xil: True
  tekshiruvli variant kamida 2 barobar sekin: True
  ⭐ shuning uchun: chegarada (API, fayl, forma) tekshiring, ichki issiq funksiyalarda emas

Nima ko'rsatdi: 2.2, 2.7-bo'limlar.

Misol 4 — Amaliy: annotatsiya qamrovi

Katta loyihaga turlarni bosqichma-bosqich kiritishda savol tug'iladi: qayerdan boshlash? Vosita modulni AST bilan tahlil qilib, qaysi funksiyalar annotatsiyalanmaganini, ular necha marta chaqirilishini (ya'ni qanchalik "markaziy" ekanini) va ommaviy API ekanini hisoblab, ustuvorlik ro'yxatini beradi.

python
"""AST: funksiyalar va ularning annotatsiya qamrovi; ommaviy/ichki; chaqiruvlar soni bo'yicha ustuvorlik; hisobot."""

import ast
from collections import Counter

MANBA = '''
"""To'lov moduli."""
from decimal import Decimal

SOLIQ = Decimal("0.12")

def komissiya(summa):
    return summa * Decimal("0.02")

def _yaxlitla(qiymat: Decimal) -> Decimal:
    return round(qiymat, 2)

def hisobla(summa: Decimal, foiz: Decimal) -> Decimal:
    return _yaxlitla(summa * foiz + komissiya(summa))

def hisobot(buyurtmalar):
    jami = Decimal(0)
    for b in buyurtmalar:
        jami += hisobla(b["summa"], SOLIQ) + komissiya(b["summa"])
    return _yaxlitla(jami)

class Hisob:
    def __init__(self, egasi, balans=Decimal(0)):
        self.egasi = egasi
        self.balans = balans

    def yech(self, summa: Decimal) -> bool:
        if summa > self.balans:
            return False
        self.balans -= summa + komissiya(summa)
        return True
'''


def qamrov(manba):
    daraxt = ast.parse(manba)
    chaqiruvlar = Counter(t.func.id for t in ast.walk(daraxt) if isinstance(t, ast.Call) and isinstance(t.func, ast.Name))
    hisobot = []
    for tugun in ast.walk(daraxt):
        if not isinstance(tugun, (ast.FunctionDef, ast.AsyncFunctionDef)):
            continue
        argumentlar = [a for a in tugun.args.args + tugun.args.kwonlyargs if a.arg not in {"self", "cls"}]
        izohlangan = sum(a.annotation is not None for a in argumentlar)
        jami = len(argumentlar) + 1                       # + qaytish qiymati
        bor = izohlangan + (tugun.returns is not None)
        hisobot.append({
            "nom": tugun.name,
            "qator": tugun.lineno,
            "ommaviy": not tugun.name.startswith("_"),
            "qamrov": bor / jami,
            "chaqiruvlar": chaqiruvlar.get(tugun.name, 0),
        })
    return hisobot


def ustuvorlik(yozuv):
    """Qamrovi past, ko'p chaqiriladigan va ommaviy funksiyalar birinchi."""
    return (yozuv["qamrov"], -yozuv["chaqiruvlar"], not yozuv["ommaviy"])


yozuvlar = qamrov(MANBA)

print("=== 1. Funksiyalar bo'yicha qamrov ===")
print(f"  {'funksiya':14} {'qator':>5} {'ommaviy':>8} {'qamrov':>7} {'chaqiruvlar':>11}")
for y in sorted(yozuvlar, key=lambda y: y["qator"]):
    print(f"  {y['nom']:14} {y['qator']:>5} {str(y['ommaviy']):>8} {y['qamrov']:>6.0%} {y['chaqiruvlar']:>11}")

jami_qamrov = sum(y["qamrov"] for y in yozuvlar) / len(yozuvlar)
print(f"\n  modul bo'yicha o'rtacha qamrov: {jami_qamrov:.0%}")
print(f"  to'liq annotatsiyalangan funksiyalar: {sum(1 for y in yozuvlar if y['qamrov'] == 1)} / {len(yozuvlar)}")

print("\n=== 2. Nimadan boshlash kerak ===")
for orin, y in enumerate(sorted(yozuvlar, key=ustuvorlik)[:3], 1):
    sabab = []
    if y["qamrov"] == 0:
        sabab.append("umuman annotatsiyasiz")
    if y["chaqiruvlar"] >= 2:
        sabab.append(f"{y['chaqiruvlar']} joyda chaqiriladi")
    if y["ommaviy"]:
        sabab.append("ommaviy API")
    print(f"  {orin}. {y['nom']} (qator {y['qator']}) — {', '.join(sabab)}")

print("\n=== 3. Tavsiya ===")
print("  ⭐ chegaradagi va ko'p chaqiriladigan funksiyalardan boshlang")
print("  ⭐ har bosqichda tekshiruvchini ishga tushiring (13.7-dars), qamrovni CI da kuzating")

Natijaning muhim qismi:

text
=== 1. Funksiyalar bo'yicha qamrov ===
  funksiya       qator  ommaviy  qamrov chaqiruvlar
  komissiya          7     True     0%           3
  _yaxlitla         10    False   100%           2
  hisobla           13     True   100%           1
  hisobot           16     True     0%           0
  __init__          23    False     0%           0
  yech              27     True   100%           0

  modul bo'yicha o'rtacha qamrov: 50%
  to'liq annotatsiyalangan funksiyalar: 3 / 6

=== 2. Nimadan boshlash kerak ===
  1. komissiya (qator 7) — umuman annotatsiyasiz, 3 joyda chaqiriladi, ommaviy API
  2. hisobot (qator 16) — umuman annotatsiyasiz, ommaviy API
  3. __init__ (qator 23) — umuman annotatsiyasiz

=== 3. Tavsiya ===
  ⭐ chegaradagi va ko'p chaqiriladigan funksiyalardan boshlang
  ⭐ har bosqichda tekshiruvchini ishga tushiring (13.7-dars), qamrovni CI da kuzating

Nima ko'rsatdi: 2.6-bo'lim.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Annotatsiya turlarni majburlaydi" Ish vaqtida tekshirilmaydi
"Annotatsiyalar kodni tezlashtiradi" CPython ulardan foydalanmaydi
"Python zaif turlangan" Kuchli turlangan: 1 + "2" — xato
"Hamma joyga annotatsiya qo'yish kerak" Chegaralar va murakkab joylar muhimroq
"Annotatsiya bo'lsa mypy avtomatik ishlaydi" Uni alohida ishga tushirasiz (CI da)
"x: int lokal o'zgaruvchi haqida ma'lumot saqlaydi" Saqlanmaydi — faqat tekshiruvchi ko'radi
"Oldinga havola uchun from __future__ import annotations shart" 3.14 da annotatsiyalar kechiktirilgan (PEP 649)
"Turlar bo'lsa testlar kerak emas" Turlar mantiqiy xatolarni ushlamaydi

6. Keng tarqalgan xatolar va yechimlari

1. Annotatsiyaga ishonib, kirishni tekshirmaslik

python
def narx_qoshish(narx: float) -> None:   # ❌ tashqi JSON dan str kelishi mumkin
    ...
narx = float(sorov["narx"])              # ✅ chegarada o'girish yoki validatsiya (13.8)

2. Ish vaqtida list[int] ni tekshirishga urinish

python
isinstance(x, list[int])                 # ❌ TypeError
isinstance(x, list) and all(isinstance(i, int) for i in x)   # ✅ (narxini biling)

3. Har chaqiruvda get_type_hints

python
def orov(*a):
    izohlar = typing.get_type_hints(f)    # ❌ har chaqiruvda qayta hisoblash
izohlar = typing.get_type_hints(f)        # ✅ dekorator yaratilganda bir marta

4. Faqat tekshiruv uchun kerak modulni oddiy import qilish

python
from ogir_modul import Tur                # ❌ ish vaqtida ham yuklanadi
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from ogir_modul import Tur            # ✅

5. Any bilan hamma narsani "tuzatish"

python
def ishla(malumot: Any) -> Any:          # ⚠️ tekshiruvchi hech narsa ayta olmaydi

6. Annotatsiyani hujjat o'rniga qo'yish

python
def yubor(x: dict) -> bool: ...          # ⚠️ qanday kalitlar? nima qaytadi?
def yubor(xabar: Xabar) -> bool:         # ✅ aniq tur + docstring
    """True — yuborildi, False — navbatga qo'yildi."""

7. Turlarni ish vaqtida hamma joyda tekshirish

python
@turlarni_tekshir                        # ⚠️ issiq funksiyada sezilarli narx
def kichik_yordamchi(x: int) -> int: ...

8. Eski uslubdagi annotatsiyalar

python
from typing import List, Dict, Optional
def f(x: List[int]) -> Optional[Dict[str, int]]: ...   # ⚠️ eskirgan uslub
def f(x: list[int]) -> dict[str, int] | None: ...      # ✅ 3.10+ (13.2, 13.3-darslar)

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 7-qism (o'tilgan): funksiya imzolari va sukut qiymatlar
  • 8.17-dars (o'tilgan): dataclass — annotatsiyalar maydonlarni belgilaydi
  • 12.1-dars (o'tilgan): annotatsiyalar kompilyatsiyada qanday saqlanadi
  • 13.2–13.6-darslar: typing vositalari, generiklar, protokollar
  • 13.7-dars: mypy bilan tekshirish va CI
  • 13.8-dars: ish vaqtidagi validatsiya (pydantic)
  • 17-qism: testlar va turlar birgalikda
  • 20–21-qismlar: FastAPI va Django — annotatsiyalarga tayangan freymvorklar

8. Eng yaxshi amaliyotlar

  1. Chegaralardan boshlang: ommaviy funksiyalar, servis kirish nuqtalari.

  2. Zamonaviy sintaksis: list[int], str | None (13.2–13.3-darslar).

  3. Annotatsiyaga ishonmang — chegarada validatsiya qiling.

  4. Statik tekshiruvchini CI ga qo'shing — aks holda annotatsiyalar eskiradi.

  5. TYPE_CHECKING bilan og'ir importlarni ish vaqtidan chiqaring.

  6. Any ni oxirgi chora sifatida ishlating va izohlang.

  7. Qamrovni o'lchang (Misol 4) va bosqichma-bosqich oshiring.

  8. Turlarni hujjat va testlar o'rnini bosuvchi deb hisoblamang.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
import typing
1.  def f(a: int) -> str:
        return a
    print(f(5))
2.  print(f.__annotations__)
3.  def g(x: "int") -> "int":
        return x
    print(typing.get_type_hints(g))
4.  def h():
        y: int = 1
        return y
    print(h.__annotations__)
5.  class K:
        a: int
        b: str = "x"
    print(K.__annotations__)
6.  print(hasattr(K, "a"), hasattr(K, "b"))
7.  print(1 + True)
8.  try:
        print(1 + "2")
    except TypeError:
        print("TypeError")
9.  print(isinstance(True, int))
10. print(type([1, 2]) is list, isinstance([1, 2], list))
11. try:
        isinstance([1], list[int])
    except TypeError:
        print("TypeError")
12. import inspect
    print(str(inspect.signature(f)))
Javoblar
  1. 5 — annotatsiya majburlamaydi
  2. {'a': <class 'int'>, 'return': <class 'str'>}
  3. {'x': <class 'int'>, 'return': <class 'int'>} — satrlar hal qilindi
  4. {} — lokal annotatsiya saqlanmaydi
  5. {'a': <class 'int'>, 'b': <class 'str'>}
  6. False True — faqat sukut qiymatli maydon atribut bo'ladi
  7. 2 — bool — int ning vorisi
  8. TypeError
  9. True
  10. True True
  11. TypeError
  12. (a: int) -> str

Vazifa 2: Xatolarni tuzating

python
1.  def narx_ol(sorov: dict) -> float:
        return sorov["narx"]          # JSON dan str kelishi mumkin

2.  from ogir_kutubxona import Model
    def bashorat(model: Model, x: list[float]) -> float:
        ...

3.  import typing
    def tekshir(f):
        def orov(*a, **k):
            izohlar = typing.get_type_hints(f)
            ...
        return orov

4.  from typing import List, Optional, Dict
    def xarita(nomlar: List[str]) -> Optional[Dict[str, int]]:
        ...

5.  def ishla(malumot: typing.Any) -> typing.Any:
        return malumot["natija"]
Javoblar
python
1.  def narx_ol(sorov: dict) -> float:
        return float(sorov["narx"])        # chegarada o'girish (yoki pydantic — 13.8)

2.  from typing import TYPE_CHECKING
    if TYPE_CHECKING:
        from ogir_kutubxona import Model   # ish vaqtida yuklanmaydi
    def bashorat(model: "Model", x: list[float]) -> float:
        ...

3.  def tekshir(f):
        izohlar = typing.get_type_hints(f)  # bir marta, dekorator yaratilganda
        def orov(*a, **k):
            ...
        return orov

4.  def xarita(nomlar: list[str]) -> dict[str, int] | None:   # zamonaviy sintaksis
        ...

5.  class Javob(typing.TypedDict):         # 13.6-dars
        natija: int
    def ishla(malumot: Javob) -> int:
        return malumot["natija"]

Vazifa 3: Annotatsiya qamrovi vositasi

Misol 4 ni CLI vositasiga aylantiring:

  1. Papkani rekursiv aylanib, har fayl uchun qamrovni hisoblasin
  2. Umumiy qamrov foizini va eng past 20 funksiyani chiqarsin
  3. --min-qamrov 0.8 bayrog'i bilan chegaradan past bo'lsa chiqish kodi 1 bo'lsin (CI uchun)
  4. Metodlar, async def, *args/**kwargs va lambda larni to'g'ri hisoblasin
  5. O'z loyihangizga qo'llang va natijani tahlil qiling

Vazifa 4: Ish vaqti tekshiruvchisi

Misol 3 dagi dekoratorni kengaytiring:

  1. list[int], dict[str, float], X | None kabi umumiy turlarni qo'llab-quvvatlasin (typing.get_origin, get_args)
  2. Tekshiruv chuqurligini cheklang (masalan, ro'yxatning faqat birinchi 10 elementi)
  3. strict=False rejimida faqat ogohlantirish (warnings) bersin
  4. functools.cache bilan izohlarni keshlang
  5. Narxini o'lchang: tekshiruvsiz, sayoz va chuqur rejimlarda

Vazifa 5: annotationlib bilan tajriba

  1. Bitta funksiya uchun VALUE, STRING, FORWARDREF formatlarini solishtiring
  2. Mavjud bo'lmagan turga havola qilgan funksiyada har uch format qanday ishlashini ko'rsating
  3. Klass, modul va metodlar uchun ham qo'llang
  4. from __future__ import annotations bilan yozilgan faylda natija qanday farq qilishini tekshiring
  5. Natijalarni jadvalda umumlashtiring

Vazifa 6: Xatolar tarixi

O'z loyihangiz (yoki ochiq kodli kichik loyiha) git tarixidan:

  1. TypeError, AttributeError, KeyError ni tuzatgan 10 ta kommitni toping
  2. Har biri uchun: annotatsiya + statik tekshiruv bu xatoni ushlay olarmidi?
  3. Ushlay oladiganlar uchun kerakli annotatsiyani yozing
  4. Natijani "turlar tizimi qancha xatoni oldini olardi" hisoboti sifatida rasmiylashtiring

Vazifa 7: O'ylash

TypeScript JavaScript ustiga bosqichma-bosqich turlar qo'shgan va bugun katta loyihalarda deyarli standart; Ruby Sorbet va RBS bilan shu yo'ldan bordi; PHP esa turlarni ish vaqtida majburlaydigan e'lonlar qo'shdi. Python annotatsiyalarni ish vaqtida tekshirmaslikni tanladi. Bu tanlovning sabablari va oqibatlari nimada, va pydantic kabi kutubxonalarning mashhurligi bu haqda nima deydi?

Javob

Qisqa javob: Python annotatsiyalarni til darajasida tekshirmaslikni tanladi, chunki tekshirish har chaqiruvda narx qo'shadi, dinamik xulqni cheklaydi va ekotizimni bo'lib yuborardi. Natijada tekshirish vositalar (mypy, pyright) va kutubxonalar (pydantic) zimmasiga o'tdi — bu moslashuvchan, lekin "annotatsiya bor, demak tekshirilgan" degan noto'g'ri tasavvurni tug'diradi.

1. Yondashuvlar

Til Turlar qayerda tekshiriladi Xususiyat
TypeScript Kompilyatsiyada (transpilyatsiya), ish vaqtida yo'q Python'ga eng yaqin model
Ruby (Sorbet/RBS) Statik + ixtiyoriy ish vaqti (sig) Aralash
PHP 7+ Ish vaqtida majburlaydi (skalyar turlar, strict_types) Xato darhol chiqadi
Java, Go, Rust Kompilyatsiyada, ishga tushirilmaydi To'liq statik
Python Statik — tashqi vositalar; ish vaqtida — kutubxonalar Bosqichma-bosqich

2. Nega Python majburlamaydi

  1. Unumdorlik: har chaqiruvda isinstance — issiq kodda sezilarli narx; list[int] ni to'liq tekshirish esa O(n)
  2. Dinamizm: duck typing, __getattr__, proksi obyektlar, mock lar — qat'iy tekshiruv ularni buzardi
  3. Moslik: milliardlab qator mavjud kod annotatsiyasiz; majburiy tekshiruv ularni sindirardi
  4. Tanlov erkinligi: turli loyihalarga turli qat'iylik kerak — mypy --strict dan to umuman tekshirmaslikgacha
  5. Til vs kutubxona: Python falsafasi bo'yicha bunday siyosat vositalarga qoldiriladi

3. Oqibatlari

Ijobiy Salbiy
Annotatsiyalar tekin (3.14 da kechiktirilgan — import tez) "Annotatsiya bor" ≠ "to'g'ri"
Har loyiha o'z qat'iyligini tanlaydi Ekotizimda bir xillik yo'q
Kutubxonalar annotatsiyalardan foydalanadi (pydantic, FastAPI, dataclasses) Ikki dunyo: statik va ish vaqti — ular mos kelmasligi mumkin
Dinamik naqshlar saqlanadi CI ga qo'shilmasa, annotatsiyalar eskiradi

4. pydantic nima haqida gapiradi

  • Dasturchilarga chegarada ishonchli validatsiya kerak: JSON, forma, konfiguratsiya, API
  • Annotatsiyalar bu ish uchun tabiiy manba bo'lib chiqdi — bir marta yozilgan tur ham statik, ham ish vaqti uchun ishlaydi
  • FastAPI muvaffaqiyati shundan: bitta imzo → validatsiya + hujjat + IDE yordami
  • Ya'ni til tekshirmasa ham, ekotizim tekshiruvni kerakli joyda tiklab oldi

5. Amaliy xulosa

  1. Ichkarida — statik tekshiruv (mypy/pyright), CI da majburiy
  2. Chegarada — ish vaqtidagi validatsiya (pydantic, qo'lda o'girish)
  3. Annotatsiyalarni "hujjat" emas, shartnoma deb qarang va ularni tekshiruvchi bilan tirik saqlang
  4. TypeScript tajribasi: bosqichma-bosqich joriy qilish ishlaydi — hammasini birdan emas

Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.


Xulosa

Bu darsda Python'ning turlash modelini va annotatsiyalarning o'rnini ko'rib chiqdik.

Eng muhim uch fikr:

  1. Python — dinamik va kuchli turlangan, annotatsiyalar esa ixtiyoriy. Interpretator def f(x: int) ni tekshirmaydi: noto'g'ri tur uzatilsa, xato ko'pincha ancha keyin va boshqa joyda chiqadi. Annotatsiyalar statik tekshiruvchilar (mypy, pyright), IDE, hujjat va pydantic kabi kutubxonalar uchun ma'lumot beradi — ya'ni foyda siz ularni ishlatganingizda paydo bo'ladi.

  2. Annotatsiyalar — oddiy ma'lumot. Funksiya, klass va modul darajasida __annotations__ da saqlanadi (lokal o'zgaruvchilarniki — hech qayerda); 3.14 da ular kechiktirilgan hisoblanadi (PEP 649) va annotationlib orqali VALUE, STRING, FORWARDREF formatlarida o'qiladi. typing.get_type_hints satrlarni hal qiladi, inspect.signature esa imzoni to'liq ko'rsatadi.

  3. Bosqichma-bosqich joriy qiling. Chegaradagi va ko'p chaqiriladigan funksiyalardan boshlang, qamrovni o'lchang, tekshiruvchini CI ga qo'shing. Ish vaqtidagi tekshiruv — chegarada (tashqi ma'lumot) o'rinli, issiq ichki funksiyalarda esa qimmat: list[int] ni isinstance bilan to'g'ridan-to'g'ri tekshirib ham bo'lmaydi.

Keyingi darsda typing modulining asoslarini o'rganamiz: zamonaviy sintaksis, kolleksiya turlari, Callable, Any va object farqi, turlar taxallusi (PEP 695) va imzolarni to'g'ri yozish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!