IlmHamroh
Python kursi/Turlar tizimi2/9-dars21 daqiqa
Mundarija (21)

13.2-dars: typing asoslari

13-QISM — TURLAR TIZIMI · 2-dars


1. Kirish va motivatsiya

13.1-darsda annotatsiyalar nima ekanini ko'rdik. Endi ularni to'g'ri yozishni o'rganamiz.

Real vaziyat. Kutubxona funksiyasining imzosi shunday edi:

python
def guruhla(yozuvlar, kalit):
    ...

Foydalanuvchilar savol berardi: yozuvlar — ro'yxatmi yoki generator ham bo'ladimi? kalit — satrmi yoki funksiyami? Nima qaytadi? Hujjatga uch abzats yozildi, savollar baribir tugamadi.

Bitta qator hammasini hal qildi:

python
def guruhla[T](yozuvlar: Iterable[T], kalit: Callable[[T], str]) -> dict[str, list[T]]:

Endi IDE avtoto'ldirish beradi, tekshiruvchi noto'g'ri chaqiruvni ushlaydi va savollar yo'qoldi.

Yaxshi annotatsiya — shartnoma: nima qabul qilinadi, nima qaytadi. Uni yozishning zamonaviy usuli 2020-yillardagi Python'da ancha soddalashdi: typing.List va Optional o'rniga list va |.

Bu darsda:

  • Zamonaviy sintaksis: list[int], dict[str, int], str | None
  • Konkret va abstrakt kolleksiyalar: list va Iterable, Sequence, Mapping
  • Callable — funksiyalarni annotatsiyalash
  • Any, object, Never, None farqi
  • Turlar taxallusi: PEP 695 type sintaksisi
  • get_type_hints, get_origin, get_args, assert_type, reveal_type
  • Amaliy: "bo'sh" annotatsiyalarni topuvchi vosita

2. Nazariya — chuqur tushuntirish

2.1. Zamonaviy sintaksis

Eski (3.8 va oldin) Zamonaviy Qachondan
List[int], Dict[str, int] list[int], dict[str, int] 3.9
Tuple[int, ...], Set[str] tuple[int, ...], set[str] 3.9
Optional[int] int | None 3.10
Union[int, str] int | str 3.10
Callable[[int], str] (typing dan) collections.abc.Callable[[int], str] 3.9
TypeVar e'lonlari def f[T](...), class K[T] 3.12 (13.4-dars)
TypeAlias type Nom = ... 3.12 (2.5)

Yangi kodda typing.List, typing.Dict, Optional, Union kerak emas — ular faqat eski kodni qo'llab-quvvatlash uchun qoldirilgan.

python
def hisobot(
    yozuvlar: list[dict[str, int]],
    sarlavha: str | None = None,
    teglar: tuple[str, ...] = (),
) -> dict[str, list[int]]: ...

2.2. Konkret va abstrakt kolleksiyalar

python
from collections.abc import Iterable, Sequence, Mapping, Callable, Iterator
Kerak bo'lgan xulq Tur
Faqat aylanib chiqish Iterable[T]
Aylanish + indeks + len Sequence[T]
Kalit bo'yicha o'qish Mapping[K, V]
O'zgartirish ham kerak list[T], dict[K, V], MutableMapping[K, V]
Iterator (bir martalik) Iterator[T]

Qoida: parametrda eng umumiy turni oling, qaytishda eng aniq turni bering.

python
def ortacha(qiymatlar: Iterable[float]) -> float: ...     # ✅ list, tuple, generator — hammasi
def ortacha(qiymatlar: list[float]) -> float: ...         # ⚠️ faqat ro'yxat

def nomlar() -> list[str]: ...                            # ✅ chaqiruvchi indekslay oladi
def nomlar() -> Iterable[str]: ...                        # ⚠️ ortiqcha cheklov

str ham Iterable[str] — shuning uchun 13.1-darsdagi xato tekshiruvchida ham ushlanmasligi mumkin: Sequence[str] yoki list[str] aniqroq.

2.3. Callable

python
Callable[[int, str], bool]      # (int, str) -> bool
Callable[[], None]              # argumentsiz
Callable[..., int]              # argumentlar ixtiyoriy, natija int

Murakkab imzolar (*args, kalitli argumentlar) uchun Protocol (13.5-dars) yoki ParamSpec (13.4-dars).

2.4. Any, object, Never, None

Tur Ma'nosi Qachon
Any "Tekshirma" — har qanday amal ruxsat Chegara, dinamik kod; kamdan-kam
object "Har qanday obyekt" — lekin amallar cheklangan Haqiqatan har narsa kelishi mumkin bo'lganda
None None qiymati (qaytish uchun -> None) Qiymat qaytarmaydigan funksiya
Never Hech qachon qiymat qaytmaydi raise bilan tugaydigan funksiya
LiteralString Faqat literal satrlar SQL/shell in'yeksiyasidan himoya
python
def xatoga_tushir(xabar: str) -> Never:
    raise RuntimeError(xabar)

def chop(qiymat: object) -> None:
    print(qiymat)                 # ✅ str(qiymat) ishlaydi
    # qiymat + 1                  # ❌ tekshiruvchi xato beradi — object da + yo'q

Any — "tekshiruvni o'chirish" tugmasi: u bilan xatolar jim o'tadi. object esa xavfsiz: undan foydalanish uchun turni toraytirish kerak (13.3-dars).

2.5. Turlar taxallusi (PEP 695)

python
type Yol = str
type Sonlar = list[float]
type Jadval[T] = dict[str, list[T]]        # generik taxallus
type Tugun = dict[str, "Tugun"]            # rekursiv — kechiktirilgan hisoblash
  • type X = ... dangasa: o'ng tomon faqat kerak bo'lganda hisoblanadi — oldinga havolalar tabiiy ishlaydi
  • Natija — TypeAliasType obyekti; X.__value__ asl turni beradi
  • Eski uslub: Yol = str (oddiy tayinlash) yoki Yol: TypeAlias = str — hali ham ishlaydi

Taxalluslar domen tilini kodga olib kiradi: type FoydalanuvchiID = int — imzolarni o'qish osonlashadi (lekin turlar baribir bir xil; alohida tur kerak bo'lsa — NewType, 13.4-dars).

2.6. Turlarni ish vaqtida tekshirish

python
typing.get_type_hints(f)          # satrlarni hal qilingan annotatsiyalar
typing.get_origin(list[int])      # list
typing.get_args(dict[str, int])   # (str, int)
typing.assert_type(x, list[int])  # tekshiruvchi uchun; ish vaqtida x ni qaytaradi
typing.reveal_type(x)             # ⚠️ ish vaqtida stderr ga yozadi

reveal_type — tekshiruvchiga "bu yerda tur nima?" degan savol; mypy uni chiqarib beradi (13.7-dars). Kodda qoldirmang.

2.7. Uslub: qanchalik aniq

Holat Tavsiya
Ommaviy funksiya To'liq annotatsiya
dict[str, Any] bilan JSON Boshlanishiga mayli; keyin TypedDict (13.6-dars)
Ichki yordamchi Qisqacha; xulosa (inference) ko'p ishni qiladi
Lokal o'zgaruvchi Odatda kerak emas — tekshiruvchi o'zi aniqlaydi
Bo'sh konteyner ballar: list[int] = [] — kerak, aks holda tur noma'lum

3. Tez ma'lumotnoma

Sintaksis

python
list[int]   dict[str, float]   tuple[int, str]   tuple[int, ...]   set[str]
str | None   int | str   Callable[[int], str]   Iterable[T]   Sequence[T]   Mapping[K, V]
type Nom = dict[str, int]      type Juft[T] = tuple[T, T]

Maxsus turlar

Any      tekshiruvni o'chiradi        object   har narsa, amallar cheklangan
None     qiymat yo'q                  Never    qaytmaydi (raise)
LiteralString   faqat literal satr

Introspeksiya

typing.get_type_hints(f)   get_origin(t)   get_args(t)
typing.assert_type(x, T)   typing.reveal_type(x)  # stderr
X.__value__ (TypeAliasType)

4. Batafsil misollar

Misol 1 — Zamonaviy sintaksis va kolleksiya turlari

python
"""Eski va yangi sintaksis; builtin generiklar; abstrakt kolleksiyalar; Callable; get_origin/get_args bilan tahlil."""

import typing
from collections.abc import Callable, Iterable, Mapping, Sequence


def hisobot(
    yozuvlar: list[dict[str, int]],
    sarlavha: str | None = None,
    teglar: tuple[str, ...] = (),
) -> dict[str, list[int]]:
    natija: dict[str, list[int]] = {}
    for y in yozuvlar:
        for kalit, qiymat in y.items():
            natija.setdefault(kalit, []).append(qiymat)
    return natija


print("=== 1. Annotatsiyalar ko'rinishi ===")
for nom, tur in typing.get_type_hints(hisobot).items():
    print(f"  {nom:10} {tur}")
print(f"  Optional[int] ko'rinishi: {typing.Optional[int]}, int | None bilan bir xil: {typing.Optional[int] == (int | None)}")
print(f"  Union[int, str] ko'rinishi: {typing.Union[int, str]}, teng: {typing.Union[int, str] == (int | str)}")

print("\n=== 2. get_origin va get_args ===")
for tur in [list[int], dict[str, float], tuple[int, ...], str | None, Callable[[int, str], bool], Sequence[int]]:
    asos = typing.get_origin(tur)
    print(f"  {str(tur):42} origin={getattr(asos, '__name__', asos)!s:12} args={typing.get_args(tur)}")

print("\n=== 3. Abstrakt va konkret ===")


def ortacha(qiymatlar: Iterable[float]) -> float:
    royxat = list(qiymatlar)
    return sum(royxat) / len(royxat)


def uchinchisi(qiymatlar: Sequence[float]) -> float:
    return qiymatlar[2]


manbalar = {
    "list": [1.0, 2.0, 3.0, 4.0],
    "tuple": (1.0, 2.0, 3.0, 4.0),
    "generator": (float(x) for x in range(1, 5)),
    "range": range(1, 5),
}
for nom, manba in manbalar.items():
    nusxa = list(manba) if nom == "generator" else manba
    indekslanadi = isinstance(nusxa, Sequence)
    print(f"  {nom:10} Iterable: {isinstance(nusxa, Iterable)!s:5} Sequence: {indekslanadi!s:5} "
          f"ortacha={ortacha(nusxa)} {'uchinchisi=' + str(uchinchisi(nusxa)) if indekslanadi else '(indekslanmaydi)'}")

print("\n=== 4. Callable ===")


def qolla(sonlar: Sequence[int], amal: Callable[[int], int]) -> list[int]:
    return [amal(s) for s in sonlar]


print(f"  kvadrat: {qolla([1, 2, 3], lambda x: x * x)}")
print(f"  abs:     {qolla([-1, -2, 3], abs)}")
print(f"  Callable argumentlari: {typing.get_args(Callable[[int, str], bool])}")

print("\n=== 5. Mapping va o'zgartirish ===")


def kalitlar(xarita: Mapping[str, int]) -> list[str]:
    return sorted(xarita)


def qoshib_yoz(xarita: dict[str, int], kalit: str, qiymat: int) -> None:
    xarita[kalit] = qiymat


narxlar = {"olma": 12000, "nok": 15000}
print(f"  Mapping (faqat o'qish) bilan: {kalitlar(narxlar)}")
qoshib_yoz(narxlar, "uzum", 21000)
print(f"  dict (o'zgartirish) bilan: {narxlar}")
print("  ⭐ parametr faqat o'qilsa — Mapping/Sequence/Iterable; o'zgartirilsa — dict/list")

Natijaning muhim qismi:

text
=== 1. Annotatsiyalar ko'rinishi ===
  yozuvlar   list[dict[str, int]]
  sarlavha   str | None
  teglar     tuple[str, ...]
  return     dict[str, list[int]]
  Optional[int] ko'rinishi: int | None, int | None bilan bir xil: True
  Union[int, str] ko'rinishi: int | str, teng: True

=== 2. get_origin va get_args ===
  list[int]                                  origin=list         args=(<class 'int'>,)
  dict[str, float]                           origin=dict         args=(<class 'str'>, <class 'float'>)
  tuple[int, ...]                            origin=tuple        args=(<class 'int'>, Ellipsis)
  str | None                                 origin=Union        args=(<class 'str'>, <class 'NoneType'>)
  collections.abc.Callable[[int, str], bool] origin=Callable     args=([<class 'int'>, <class 'str'>], <class 'bool'>)
  collections.abc.Sequence[int]              origin=Sequence     args=(<class 'int'>,)

=== 3. Abstrakt va konkret ===
  list       Iterable: True  Sequence: True  ortacha=2.5 uchinchisi=3.0
  tuple      Iterable: True  Sequence: True  ortacha=2.5 uchinchisi=3.0
  generator  Iterable: True  Sequence: True  ortacha=2.5 uchinchisi=3.0
  range      Iterable: True  Sequence: True  ortacha=2.5 uchinchisi=3

=== 4. Callable ===
  kvadrat: [1, 4, 9]
  abs:     [1, 2, 3]
  Callable argumentlari: ([<class 'int'>, <class 'str'>], <class 'bool'>)

=== 5. Mapping va o'zgartirish ===
  Mapping (faqat o'qish) bilan: ['nok', 'olma']
  dict (o'zgartirish) bilan: {'olma': 12000, 'nok': 15000, 'uzum': 21000}
  ⭐ parametr faqat o'qilsa — Mapping/Sequence/Iterable; o'zgartirilsa — dict/list

Nima ko'rsatdi: 2.1–2.3-bo'limlar.

Misol 2 — Any, object, Never, None

python
"""Any va object farqi; toraytirish; Never; -> None; ish vaqtidagi xulq va tekshiruvchi nuqtai nazari."""

import typing


def any_bilan(qiymat: typing.Any) -> int:
    """Tekshiruvchi bu yerda hech narsani tekshirmaydi."""
    return qiymat + 1                      # tekshiruvchi: OK (lekin ish vaqtida xato bo'lishi mumkin)


def object_bilan(qiymat: object) -> int:
    """Tekshiruvchi: object ustida + ishlatib bo'lmaydi — avval toraytirish kerak."""
    if isinstance(qiymat, int):
        return qiymat + 1                  # ✅ bu yerda qiymat — int
    if isinstance(qiymat, str) and qiymat.isdigit():
        return int(qiymat) + 1
    raise TypeError(f"son kutilgan edi, {type(qiymat).__name__} keldi")


print("=== 1. Any va object ===")
print(f"  any_bilan(41) = {any_bilan(41)}")
try:
    any_bilan("matn")
except TypeError as xato:
    print(f"  any_bilan('matn') → TypeError: {xato}  ⚠️ tekshiruvchi ogohlantirmagan bo'lardi")
print(f"  object_bilan(41) = {object_bilan(41)}, object_bilan('41') = {object_bilan('41')}")
try:
    object_bilan([1])
except TypeError as xato:
    print(f"  object_bilan([1]) → TypeError: {xato}  ✅ xato aniq va kutilgan")

print("\n=== 2. Never ===")


def qollab_quvvatlanmaydi(format_nomi: str) -> typing.Never:
    raise NotImplementedError(f"{format_nomi} formati qo'llab-quvvatlanmaydi")


def yukla(format_nomi: str) -> list[str]:
    if format_nomi == "csv":
        return ["a", "b"]
    return qollab_quvvatlanmaydi(format_nomi)       # tekshiruvchi: bu yo'l qaytmaydi


print(f"  yukla('csv') = {yukla('csv')}")
try:
    yukla("xml")
except NotImplementedError as xato:
    print(f"  yukla('xml') → NotImplementedError: {xato}")
print(f"  annotatsiya: {typing.get_type_hints(qollab_quvvatlanmaydi)['return']}")

print("\n=== 3. None va -> None ===")


def saqla(yozuv: dict[str, int]) -> None:
    yozuv["saqlandi"] = 1


natija = saqla({"a": 1})
print(f"  -> None funksiyasi qaytargani: {natija!r}")
print(f"  annotatsiyada None turi: {typing.get_type_hints(saqla)['return']}")
print(f"  None ning turi: {type(None).__name__}")

print("\n=== 4. Qachon qaysi biri ===")
jadval = [
    ("Any", "tashqi dinamik ma'lumot, tekshiruvni ataylab o'chirish", "kamdan-kam, izoh bilan"),
    ("object", "har narsa kelishi mumkin, lekin xavfsiz ishlash kerak", "toraytirish bilan"),
    ("None", "funksiya qiymat qaytarmaydi", "har doim yozing"),
    ("Never", "funksiya hech qachon qaytmaydi (raise)", "xato yordamchilarida"),
]
for tur, qachon, tavsiya in jadval:
    print(f"  {tur:8} {qachon:52} → {tavsiya}")

Natijaning muhim qismi:

text
=== 1. Any va object ===
  any_bilan(41) = 42
  any_bilan('matn') → TypeError: can only concatenate str (not "int") to str  ⚠️ tekshiruvchi ogohlantirmagan bo'lardi
  object_bilan(41) = 42, object_bilan('41') = 42
  object_bilan([1]) → TypeError: son kutilgan edi, list keldi  ✅ xato aniq va kutilgan

=== 2. Never ===
  yukla('csv') = ['a', 'b']
  yukla('xml') → NotImplementedError: xml formati qo'llab-quvvatlanmaydi
  annotatsiya: typing.Never

=== 3. None va -> None ===
  -> None funksiyasi qaytargani: None
  annotatsiyada None turi: <class 'NoneType'>
  None ning turi: NoneType

=== 4. Qachon qaysi biri ===
  Any      tashqi dinamik ma'lumot, tekshiruvni ataylab o'chirish → kamdan-kam, izoh bilan
  object   har narsa kelishi mumkin, lekin xavfsiz ishlash kerak → toraytirish bilan
  None     funksiya qiymat qaytarmaydi                          → har doim yozing
  Never    funksiya hech qachon qaytmaydi (raise)               → xato yordamchilarida

Nima ko'rsatdi: 2.4-bo'lim.

Misol 3 — Turlar taxallusi (PEP 695)

python
"""type sintaksisi; TypeAliasType va __value__; dangasa hisoblash va rekursiv taxalluslar; generik taxallus; domen tili."""

import typing

type FoydalanuvchiID = int
type Manzil = str
type Ballar = dict[FoydalanuvchiID, list[float]]
type JSON = dict[str, "JSON"] | list["JSON"] | str | int | float | bool | None
type Juft[T] = tuple[T, T]


def ortacha_ball(ballar: Ballar, kim: FoydalanuvchiID) -> float:
    qiymatlar = ballar.get(kim, [])
    return round(sum(qiymatlar) / len(qiymatlar), 2) if qiymatlar else 0.0


def chegaralar[T](qiymatlar: list[T]) -> Juft[T]:
    return qiymatlar[0], qiymatlar[-1]


print("=== 1. Taxallus obyekti ===")
print(f"  FoydalanuvchiID turi: {type(FoydalanuvchiID).__name__}, nomi: {FoydalanuvchiID.__name__}")
print(f"  __value__: {FoydalanuvchiID.__value__}, ya'ni {FoydalanuvchiID.__value__.__name__}")
print(f"  Ballar.__value__: {Ballar.__value__}")
print(f"  ⚠️ ish vaqtida FoydalanuvchiID va int bir xil tur: {FoydalanuvchiID.__value__ is int}")

print("\n=== 2. Dangasa hisoblash va rekursiya ===")
print(f"  JSON taxallusi rekursiv: {'JSON' in str(JSON.__value__)}")
print(f"  hisoblanganda xato bermadi: {type(JSON.__value__).__name__}")


type KeyinAniqlanadi = list[KeyinchalikKlass]


class KeyinchalikKlass:
    nom: str


print(f"  keyin aniqlanadigan klassga havola: {KeyinAniqlanadi.__value__}")

print("\n=== 3. Generik taxallus ===")
print(f"  Juft: {Juft}, tur parametrlari: {Juft.__type_params__}")
print(f"  Juft[int]: {Juft[int]}")
print(f"  chegaralar([3, 9, 5, 7]) = {chegaralar([3, 9, 5, 7])}")
print(f"  chegaralar(['a', 'b', 'c']) = {chegaralar(['a', 'b', 'c'])}")

print("\n=== 4. Imzolarda o'qilishi ===")
print(f"  ortacha_ball izohlari: {ortacha_ball.__annotations__}")
print(f"  get_type_hints: {typing.get_type_hints(ortacha_ball)}")
ballar: Ballar = {101: [4.5, 5.0, 4.0], 102: [3.0]}
print(f"  ortacha_ball(ballar, 101) = {ortacha_ball(ballar, 101)}, yo'q foydalanuvchi: {ortacha_ball(ballar, 999)}")

print("\n=== 5. Domen tili ===")
type Soniya = float
type Millisekund = float


def kutish(vaqt: Soniya) -> Millisekund:
    return vaqt * 1000


print(f"  kutish(1.5) = {kutish(1.5)}")
print("  ⚠️ taxallus faqat o'qish uchun: ish vaqtida ham, tekshiruvchida ham Soniya va Millisekund — float")
print("  ✅ haqiqiy ajratish kerak bo'lsa — NewType (13.4-dars)")

Natijaning muhim qismi:

text
=== 1. Taxallus obyekti ===
  FoydalanuvchiID turi: TypeAliasType, nomi: FoydalanuvchiID
  __value__: <class 'int'>, ya'ni int
  Ballar.__value__: dict[FoydalanuvchiID, list[float]]
  ⚠️ ish vaqtida FoydalanuvchiID va int bir xil tur: True

=== 2. Dangasa hisoblash va rekursiya ===
  JSON taxallusi rekursiv: True
  hisoblanganda xato bermadi: Union
  keyin aniqlanadigan klassga havola: list[__main__.KeyinchalikKlass]

=== 3. Generik taxallus ===
  Juft: Juft, tur parametrlari: (T,)
  Juft[int]: Juft[int]
  chegaralar([3, 9, 5, 7]) = (3, 7)
  chegaralar(['a', 'b', 'c']) = ('a', 'c')

=== 4. Imzolarda o'qilishi ===
  ortacha_ball izohlari: {'ballar': Ballar, 'kim': FoydalanuvchiID, 'return': <class 'float'>}
  get_type_hints: {'ballar': Ballar, 'kim': FoydalanuvchiID, 'return': <class 'float'>}
  ortacha_ball(ballar, 101) = 4.5, yo'q foydalanuvchi: 0.0

=== 5. Domen tili ===
  kutish(1.5) = 1500.0
  ⚠️ taxallus faqat o'qish uchun: ish vaqtida ham, tekshiruvchida ham Soniya va Millisekund — float
  ✅ haqiqiy ajratish kerak bo'lsa — NewType (13.4-dars)

Nima ko'rsatdi: 2.5-bo'lim.

Misol 4 — Amaliy: "bo'sh" annotatsiyalarni topuvchi

Annotatsiyalar bor, lekin foydasi yo'q holat ko'p uchraydi: Any, parametrsiz list, dict, tuple. Vosita modulni AST bilan tekshirib, shunday joylarni topadi va aniqroq variantni taklif qiladi.

python
"""AST: Any, parametrsiz konteynerlar, eskirgan typing.List/Optional; tavsiyalar bilan hisobot."""

import ast

PARAMETRSIZ = {"list", "dict", "tuple", "set", "frozenset"}
ESKIRGAN = {"List": "list", "Dict": "dict", "Tuple": "tuple", "Set": "set",
            "Optional": "X | None", "Union": "X | Y", "Callable": "collections.abc.Callable"}


class Tekshiruvchi(ast.NodeVisitor):
    def __init__(self):
        self.topilmalar = []

    def izoh_tekshir(self, tugun, joy):
        if tugun is None:
            self.topilmalar.append((joy[1], joy[0], "annotatsiya yo'q", "turni yozing"))
            return
        matn = ast.unparse(tugun)
        if matn == "Any" or matn.endswith(".Any"):
            self.topilmalar.append((joy[1], joy[0], "Any", "aniq tur yoki object + toraytirish"))
        elif matn in PARAMETRSIZ:
            self.topilmalar.append((joy[1], joy[0], f"parametrsiz {matn}", f"{matn}[...] ko'rinishida yozing"))
        else:
            asos = matn.split("[")[0].split(".")[-1]
            if asos in ESKIRGAN:
                self.topilmalar.append((joy[1], joy[0], f"eskirgan {asos}", f"{ESKIRGAN[asos]} ishlating"))

    def visit_FunctionDef(self, tugun):
        joy = (tugun.name, tugun.lineno)
        for a in tugun.args.args + tugun.args.kwonlyargs:
            if a.arg not in {"self", "cls"}:
                self.izoh_tekshir(a.annotation, (f"{tugun.name}({a.arg})", tugun.lineno))
        self.izoh_tekshir(tugun.returns, (f"{tugun.name}() -> ", tugun.lineno))
        self.generic_visit(tugun)

    visit_AsyncFunctionDef = visit_FunctionDef


MANBA = '''
from typing import Any, Optional, List

def yukla(yol: str) -> dict:
    ...

def qayta_ishla(yozuvlar: List[dict], sozlama: Optional[dict] = None) -> Any:
    ...

def xulosa(yozuvlar: list[dict[str, float]], chegara: float = 0.0) -> dict[str, float]:
    ...

def yubor(manzillar, xabar: str) -> None:
    ...

class Servis:
    def ishga_tushir(self, port: int = 8000) -> None:
        ...
'''

tekshiruvchi = Tekshiruvchi()
tekshiruvchi.visit(ast.parse(MANBA))

print("=== Annotatsiya sifati hisoboti ===")
print(f"  {'qator':>5}  {'joy':28} {'muammo':22} tavsiya")
for qator, joy, muammo, tavsiya in sorted(tekshiruvchi.topilmalar):
    print(f"  {qator:>5}  {joy:28} {muammo:22} {tavsiya}")

turlar = {}
for _, _, muammo, _ in tekshiruvchi.topilmalar:
    kalit = muammo.split()[0]
    turlar[kalit] = turlar.get(kalit, 0) + 1
print(f"\n  jami {len(tekshiruvchi.topilmalar)} ta muammo: {dict(sorted(turlar.items()))}")
print("  ⭐ 'xulosa' funksiyasi hisobotga tushmadi — uning annotatsiyalari to'liq va aniq")

Natijaning muhim qismi:

text
=== Annotatsiya sifati hisoboti ===
  qator  joy                          muammo                 tavsiya
      4  yukla() ->                   parametrsiz dict       dict[...] ko'rinishida yozing
      7  qayta_ishla() ->             Any                    aniq tur yoki object + toraytirish
      7  qayta_ishla(sozlama)         eskirgan Optional      X | None ishlating
      7  qayta_ishla(yozuvlar)        eskirgan List          list ishlating
     13  yubor(manzillar)             annotatsiya yo'q       turni yozing

  jami 5 ta muammo: {'Any': 1, 'annotatsiya': 1, 'eskirgan': 2, 'parametrsiz': 1}
  ⭐ 'xulosa' funksiyasi hisobotga tushmadi — uning annotatsiyalari to'liq va aniq

Nima ko'rsatdi: 2.1, 2.4, 2.7-bo'limlar.


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

Noto'g'ri fikr To'g'risi
"typing.List va list — turli narsa" Bir xil ma'no; typing.List eskirgan
"Any — xavfsiz sukut qiymat" U tekshiruvni o'chiradi; object xavfsizroq
"Parametrda list yozish yaxshi" Faqat o'qilsa — Iterable/Sequence moslashuvchan
"dict deb yozsam yetarli" dict[str, int] — aniq; parametrsiz dict ma'lumot bermaydi
"Taxallus yangi tur yaratadi" Yo'q — faqat nom; yangi tur uchun NewType
"-> None ni yozmasa ham bo'ladi" Yozilmasa, tekshiruvchi funksiyani tekshirmasligi mumkin
"Never va None bir xil" None — qiymat qaytaradi (None); Never — umuman qaytmaydi
"reveal_type ni kodda qoldirsa bo'ladi" Ish vaqtida stderr ga yozadi — faqat tekshiruv paytida

6. Keng tarqalgan xatolar va yechimlari

1. Parametrsiz konteyner

python
def yukla(yol: str) -> dict:                 # ❌ qanday dict?
def yukla(yol: str) -> dict[str, float]:     # ✅

2. Parametrda juda tor tur

python
def ortacha(qiymatlar: list[float]) -> float:      # ⚠️ generator bilan ishlamaydi
def ortacha(qiymatlar: Iterable[float]) -> float:  # ✅

3. Qaytishda juda umumiy tur

python
def nomlar() -> Iterable[str]:               # ⚠️ chaqiruvchi indekslay olmaydi
def nomlar() -> list[str]:                   # ✅

4. Any bilan xatolarni yashirish

python
def ishla(malumot: Any) -> Any: ...          # ❌ tekshiruvchi jim
def ishla(malumot: Yozuv) -> Natija: ...     # ✅

5. Bo'sh konteynerni annotatsiyasiz qoldirish

python
ballar = []                                   # ⚠️ tekshiruvchi: list[Unknown]
ballar: list[int] = []                        # ✅

6. str ni Iterable[str] sifatida qabul qilish

python
def yubor(manzillar: Iterable[str]) -> None:  # ⚠️ str ham mos keladi (13.1-dars xatosi)
def yubor(manzillar: Sequence[str]) -> None:  # ⚠️ str yana mos!
def yubor(manzillar: list[str]) -> None:      # ✅ eng aniq

7. Eskirgan importlar

python
from typing import List, Optional             # ⚠️ yangi kodda kerak emas

8. Callable ni murakkab imzolar uchun ishlatish

python
ishlov: Callable[..., None]                   # ⚠️ argumentlar yo'qoldi
class Ishlov(Protocol):                       # ✅ 13.5-dars
    def __call__(self, xabar: str, *, qayta: int = 0) -> None: ...

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 6-qism (o'tilgan): ma'lumot tuzilmalari — Sequence, Mapping protokollari
  • 10.1–10.2-darslar (o'tilgan): iterator va iterable — Iterable turi shundan
  • 13.1-dars (o'tilgan): annotatsiyalar nima
  • 13.3-dars: Optional, Union, Literal va toraytirish
  • 13.4-dars: generiklar, TypeVar, NewType, ParamSpec
  • 13.5–13.6-darslar: Protocol, TypedDict
  • 15-qism: standart kutubxona imzolarini o'qish
  • 20-qism: FastAPI — annotatsiyalardan API hujjati

8. Eng yaxshi amaliyotlar

  1. Zamonaviy sintaksis: list[int], str | None, type Nom = ....

  2. Parametrda umumiy, qaytishda aniq tur.

  3. Any o'rniga object + toraytirish.

  4. Bo'sh konteynerlarni annotatsiyalang.

  5. Murakkab tuzilmalarga taxallus bering — imzolar qisqaradi.

  6. Never bilan "qaytmaydigan" funksiyalarni belgilang.

  7. reveal_type ni faqat tekshiruv paytida ishlating.

  8. Annotatsiya sifatini avtomatik tekshiring (Misol 4) va CI ga qo'shing.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
import typing
from collections.abc import Iterable, Sequence
1.  print(typing.get_origin(list[int]))
2.  print(typing.get_args(dict[str, int]))
3.  print(typing.Optional[int] == (int | None))
4.  print(typing.get_args(int | str | None))
5.  type Yol = str
    print(type(Yol).__name__)
6.  print(Yol.__value__)
7.  type Juft[T] = tuple[T, T]
    print(Juft.__type_params__)
8.  print(isinstance("abc", Iterable), isinstance("abc", Sequence))
9.  print(isinstance((1, 2), Sequence), isinstance({1, 2}, Sequence))
10. def f() -> None:
        pass
    print(f() is None, typing.get_type_hints(f))
11. print(typing.get_origin(Sequence[int]).__name__)
12. x: list[int] = [1, 2]
    print(typing.assert_type(x, list[int]) == x)
Javoblar
  1. <class 'list'>
  2. (<class 'str'>, <class 'int'>)
  3. True
  4. (<class 'int'>, <class 'str'>, <class 'NoneType'>)
  5. TypeAliasType
  6. <class 'str'>
  7. (T,)
  8. True True — satr ham ketma-ketlik
  9. True False — to'plam indekslanmaydi
  10. True {'return': <class 'NoneType'>}
  11. Sequence
  12. True — assert_type qiymatni o'zgarishsiz qaytaradi

Vazifa 2: Xatolarni tuzating

python
1.  from typing import List, Dict, Optional
    def guruhla(yozuvlar: List[Dict], kalit: str) -> Optional[Dict]:
        ...

2.  def ortacha(qiymatlar: list[float]) -> float:
        return sum(qiymatlar) / len(qiymatlar)     # generator bilan ham ishlatiladi

3.  def sozlama_yukla(yol: str) -> dict:
        ...

4.  def qayta_ishla(malumot: typing.Any) -> typing.Any:
        return malumot["natija"] * 2

5.  natijalar = []
    def qosh(x: int) -> None:
        natijalar.append(x)
Javoblar
python
1.  def guruhla(yozuvlar: list[dict[str, int]], kalit: str) -> dict[str, list[int]] | None:
        ...

2.  from collections.abc import Iterable
    def ortacha(qiymatlar: Iterable[float]) -> float:
        royxat = list(qiymatlar)                    # generatorni bir marta o'qiymiz
        return sum(royxat) / len(royxat)

3.  def sozlama_yukla(yol: str) -> dict[str, str | int | bool]:
        ...
    # yoki aniqroq: TypedDict (13.6-dars)

4.  def qayta_ishla(malumot: dict[str, int]) -> int:
        return malumot["natija"] * 2

5.  natijalar: list[int] = []                       # bo'sh konteyner annotatsiyalanadi
    def qosh(x: int) -> None:
        natijalar.append(x)

Vazifa 3: Kutubxona imzolarini yozish

Quyidagi funksiyalar uchun to'liq annotatsiya yozing va nima uchun shunday tanlaganingizni izohlang:

  1. guruhla(yozuvlar, kalit_funksiya) — elementlarni kalit bo'yicha guruhlaydi
  2. birinchi_mos(elementlar, shart, sukut=None) — shartga mos birinchi element
  3. qayta_urin(funksiya, urinishlar=3, kutish=1.0) — funksiyani qayta chaqiradi
  4. oqim_oqi(fayl, bolak_hajmi=8192) — bo'laklab o'qiydi
  5. xarita_birlashtir(*xaritalar) — bir nechta lug'atni birlashtiradi

Vazifa 4: Taxalluslar lug'ati

O'z loyihangiz (yoki o'quv loyihasi) uchun domen taxalluslari to'plamini yarating:

  1. Kamida 8 ta taxallus: identifikatorlar, vaqt birliklari, pul, JSON tuzilmalari
  2. Ularni turlar.py moduliga joylang va imzolarda ishlating
  3. Rekursiv taxallus (JSON) yozing va uni ishlatib ko'ring
  4. NewType bilan taqqoslang: qaysi hollarda taxallus yetarli emas (13.4-darsga tayyorgarlik)

Vazifa 5: Annotatsiya sifati vositasi

Misol 4 ni rivojlantiring:

  1. Sinf atributlari va modul darajasidagi o'zgaruvchilarni ham tekshirsin
  2. # type: ignore va # noqa izohlarini hurmat qilsin
  3. Muammo darajalarini joriy qiling: xato (Any), ogohlantirish (parametrsiz konteyner), maslahat (eskirgan import)
  4. JSON hisobot va CI uchun chiqish kodi qo'shing
  5. O'z loyihangizga qo'llab, birinchi 20 muammoni tuzating

Vazifa 6: Abstraktdan konkretga

Bitta funksiyaning to'rt variantini yozing: list, Sequence, Iterable, Iterator parametri bilan:

  1. Har biri qanday manbalar bilan ishlashini jadvalda ko'rsating (ro'yxat, kortej, generator, range, fayl obyekti)
  2. Qaysi variantda ikki marta aylanib chiqish mumkinligini ko'rsating
  3. Generatorni ikki marta aylanishga urinib, natijani tushuntiring
  4. Har variant uchun mos real vaziyat keltiring

Vazifa 7: O'ylash

TypeScript'da any va unknown bor — unknown bilan ishlash uchun turni toraytirish shart; Java'da Object va generiklar tur o'chirish (type erasure) bilan; Go'da any (interface{}) va turlar tasdiqlash (type assertion). Python'dagi Any va object shu vositalarga qanday mos keladi, va nega Any "yuqumli" (viral) deb ataladi?

Javob

Qisqa javob: Any — TypeScript'dagi any ning aynan o'zi: u bilan har qanday amal ruxsat etiladi va natija ham Any bo'ladi, shuning uchun tekshiruv "o'chgan zona" kod bo'ylab tarqaladi. object esa TypeScript'dagi unknown ga o'xshaydi: qiymat bor, lekin uni ishlatish uchun avval turni toraytirish kerak.

1. Taqqoslash

Python TypeScript Java Go Ma'nosi
Any any (yo'q) interface{} + tekshiruvsiz Tekshiruvni o'chirish
object unknown Object any + type assertion Har narsa, lekin avval toraytirish
isinstance bilan toraytirish typeof, instanceof instanceof + cast type switch Turni aniqlash
Never never (yo'q) (yo'q) Qaytmaydigan yo'l

2. Nega Any yuqumli

python
def yukla(yol: str) -> Any: ...

malumot = yukla("a.json")     # malumot: Any
soni = malumot["yozuvlar"]    # soni: Any  ← tekshiruv yo'q
jami = soni + 1               # jami: Any  ← xato bo'lsa ham jim

Har amal natijasi yana Any bo'lgani uchun "o'chgan zona" butun quvur bo'ylab tarqaladi. Bir nechta Any katta loyihada tekshiruvni amalda foydasiz qilishi mumkin — mypy da --disallow-any-expr, warn_return_any kabi bayroqlar shuning uchun bor (13.7-dars).

3. object bilan ishlash

python
def chop(qiymat: object) -> str:
    if isinstance(qiymat, str):
        return qiymat.upper()        # bu yerda str
    if isinstance(qiymat, (int, float)):
        return f"{qiymat:.2f}"
    return repr(qiymat)              # object da ham repr bor

Toraytirish (narrowing) — tekshiruvchi isinstance, is None, assert, match kabi konstruksiyalarni tushunadi (13.3-dars).

4. Java va Go tajribasi

  • Java'da generiklar tur o'chirish bilan: ish vaqtida List<String> ham List — Python'ga o'xshash (annotatsiyalar ish vaqtida majburlamaydi)
  • Go'da interface{} (hozir any) uzoq vaqt yagona yo'l edi; generiklar 1.18 da qo'shildi — "turlarsiz moslashuvchanlik" narxi hammaga tanish
  • Ikkala tilda ham amaliyot bir xil: umumiy turdan foydalanishdan oldin aniq tekshiruv

5. Amaliy qoidalar

  1. Any ni faqat chegarada va izoh bilan ishlating (# tashqi kutubxona imzosi yo'q)
  2. Ichkariga Any tarqalmasin: chegarada aniq turga o'giring (pydantic, qo'lda tekshiruv)
  3. "Nima kelishini bilmayman" holatida object — tekshiruvchi sizni toraytirishga majburlaydi
  4. mypy --strict da Any qaytaruvchi funksiyalar ogohlantiriladi — buni yoqing

Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.


Xulosa

Bu darsda typing ning kundalik asoslarini o'rgandik.

Eng muhim uch fikr:

  1. Zamonaviy sintaksis sodda. list[int], dict[str, float], tuple[int, ...], str | None — typing.List va Optional yangi kodda kerak emas. Parametrlarda Iterable, Sequence, Mapping kabi abstrakt turlar moslashuvchanlik beradi, qaytishda esa aniq list/dict chaqiruvchiga ko'proq imkon beradi. Funksiyalar uchun Callable[[int], str], murakkab imzolar uchun Protocol.

  2. Any — tekshiruvni o'chiradi, object — xavfsiz. Any bilan har qanday amal ruxsat etiladi va natija yana Any bo'lib tarqaladi; object esa turni toraytirishga majbur qiladi. -> None — qiymat qaytarmaydigan funksiya, -> Never — umuman qaytmaydigan (raise) funksiya.

  3. Taxalluslar domen tilini beradi. type Ballar = dict[int, list[float]] uzun imzolarni qisqartiradi, type sintaksisi (PEP 695) dangasa hisoblanadi — rekursiv va oldinga havolalar tabiiy ishlaydi, X.__value__ asl turni ko'rsatadi. Lekin taxallus yangi tur yaratmaydi: Soniya va Millisekund baribir float — haqiqiy ajratish uchun NewType kerak.

Keyingi darsda ixtiyoriy qiymatlar va variantlar bilan ishlaymiz: str | None, birlashmalar, Literal, Enum bilan solishtirish va eng muhimi — turni toraytirish (narrowing) qoidalari.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
13.2-dars: typing asoslari — IlmHamroh