Ачааллж байна...
Өмнөх хичээлд бид төрлүүдийг нарийн тодорхойлж сурсан: int | str, Literal, TypedDict. Тэр бүх hint нэг нийтлэг шинжтэй байсан: төрөл тогтмол. parse_amount үргэлж int буцаадаг, find_student үргэлж Student эсвэл None. Гэвч зарим код төрлөөс огт хамаардаггүй. Жагсаалтын эхний элементийг авах функц тоо, нэр, дуу, захиалга, юу ч агуулсан жагсаалттай адилхан ажиллана. Хичээл 8.2-ын Playlist шиг сав-ыг ерөнхийлж, ямар ч зүйл хадгалдаг болгож болно. Ийм кодыг type hint-ээр хэрхэн тодорхойлох вэ?
Хамгийн шууд хариулт нь "юу ч" гэсэн утгатай Any төрөл. Гэвч энэ нь чухал мэдээллийг алддаг. Хэрэв жагсаалтын эхний элементийг авдаг функц нэрсийн жагсаалт хүлээн авбал тэр нэр буцаана, тоо биш. "Юу оруулна, тэр л гарна" гэсэн энэ амлалт Any-д байхгүй. Энэ хичээлийн гол хэрэгсэл бол TypeVar буюу "төрлийн хувьсагч": тодорхой төрлийн оронд түр орлогч тэмдэг, дуудлага бүрт тодорхой төрлөөр солигддог. TypeVar ашигладаг функц, class-уудыг generic буюу "ерөнхий" гэж нэрлэдэг.
Энэ санаа эхлээд хийсвэр санагдаж магадгүй, тиймээс бид маш удаан, энгийнээс эхэлнэ. Эхлээд Any-ийн асуудлыг харуулна. Дараа нь generic функц бичиж, type checker түүнийг хэрхэн "бодож" байгааг алхам алхмаар мөрдөнө. Дараа нь Stack буюу "овоолго" class-ийг generic болгож, нэг нэгээр нь method нэмнэ. Эцэст нь "ямар ч төрөл, гэхдээ тодорхой чадвартай" гэсэн хязгаарлалтыг сурна. Python 3.12-оос хойш generic бичих шинэ, илүү цэвэр бичлэг бий. Бид түүнийг үндсэн хэлбэр болгож заах бөгөөд бусдын кодонд одоо ч түгээмэл харагддаг хуучин бичлэгийг таних түвшинд харуулна.
Театрын хувцасны өлгүүрийг төсөөлөөд үзээрэй. Та хүрмээ өгөхөд ажилтан дугаартай жетон өгнө. Тоглолтын дараа жетоноо өгөхөд таны хүрэм буцаж ирнэ: өөр хүний хүрэм ч биш, малгай ч биш. Өлгүүрийн ажилтан хүрэм, цув, ороолт, цүнхтэй адилхан ажилладаг: тэр юу хүлээн авснаа нарийн мэдэх шаардлагагүй. Гэвч тэр нэг амлалтыг үргэлж биелүүлдэг: хүлээн авсан зүйлээ яг тэр хэвээр нь буцааж өгнө.
Одоо өлгүүрийн ажилтанд хоёр төрлийн заавар өгч болохыг харьцуулъя. Эхний заавар: "Юу ч хүлээн ав, юу ч буцааж өг." Энэ заавраар ажилтан таны хүрмийн оронд шүхэр өгсөн ч зааврыг зөрчөөгүй. Энэ бол Any. Хоёр дахь заавар: "Хүмүүс юу өгнө, тэр л буцааж өг." Энэ заавар хүлээн авах зүйлийг хязгаарлаагүй ч оролт ба гаралтыг холбосон. Энэ бол TypeVar.
Өөр нэг дүрслэл: шүүгээний загвар. Үйлдвэр нэг загвараар олон шүүгээ хийдэг, загвар өөрөө "юу хадгалах вэ"-г заагаагүй. Гэвч та шүүгээ худалдаж авахдаа түүнд шошго наана: "гутал". Тэр мөчөөс эхлэн тэр шүүгээнд гутал хийнэ, гутал гаргана. Загвар бол generic class, шошготой шүүгээ бол Stack[int] шиг тодорхой төрөлтэй хэрэглээ.
Жагсаалтын эхний элементийг буцаадаг функц бичье. Ямар hint өгөх вэ?
python
from typing import Any
def first(items: list[Any]) -> Any:
return items[0]
names = ["Бат", "Сараа"]
scores = [95, 88]
name = first(names)
score = first(scores)
print(name.upper())
print(score.upper())БАТ
Traceback (most recent call last):
...
AttributeError: 'int' object has no attribute 'upper'list[Any] нь "юу ч агуулсан жагсаалт", буцаах Any нь "юу ч". Функц ажилласан, гэвч hint-үүд type checker-т юу ч хэлээгүй. Any бол type checker-ийн хувьд "энэ утгыг шалгахаа больж бай" гэсэн тушаал. name ч, score ч Any гэж тооцогдох тул score.upper()-д mypy ямар ч анхааруулга өгөхгүй, алдаа зөвхөн ажиллах үед гарна. Бид hint бичсэн ч түүний бүх ашгийг алдсан.
Харин функцийн утгыг хараарай: тэр жагсаалтын элементийн төрлийг өөрчлөхгүй буцаадаг. str-ийн жагсаалтаас str, int-ийн жагсаалтаас int. Энэ холбоог hint-д бичих хэрэгтэй.
Python 3.12-оос хойшх бичлэгээр:
python
def first[T](items: list[T]) -> T:
return items[0]
name = first(["Бат", "Сараа"])
score = first([95, 88])
print(name.upper())
print(score + 5)БАТ
100Функцийн нэрийн ард дөрвөлжин хаалтанд [T] бичигдсэн: энэ бол "энэ функц T гэсэн төрлийн хувьсагчтай" гэсэн мэдэгдэл. T нь тодорхой төрөл биш, түр орлогч. Дараа нь T-г гарын үсэгт хэрэглэсэн: items бол T-ийн жагсаалт, буцаах утга нь T. Хоёр газарт ижил T байгаа нь "оролтын элемент ба гаралт нэг ижил төрөл" гэсэн амлалт. Нэр нь заавал T байх албагүй, гэхдээ уламжлал ёсоор нэг том үсэг хэрэглэдэг: T нь “type”-ийн эхний үсэг.
Trace: type checker хэрхэн боддог вэ
Энэ хичээлд нуугдмал төлөв нь ажиллах үед биш, type checker-ийн толгойд байна. Ажиллах үед first энгийн функц, T огт хэрэглэгдэхгүй. Харин mypy кодыг шалгахдаа ямар дүгнэлт хийдгийг алхам алхмаар мөрдье.
Алхам 1 (функцийн гарын үсгийг унших). mypy first[T](items: list[T]) -> T-ийг уншиж, "энэ функцийг дуудах бүрт T-д нэг тодорхой төрөл онооно, тэр нь дуудлагаас дуудлагад өөр байж болно" гэж тэмдэглэнэ.
Алхам 2 (first(["Бат", "Сараа"])). mypy argument-ийг харна: list[str]. Үүнийг list[T]-тэй тулгана: T = str байх ёстой. Энэ дуудлагад: T → str. Буцаах төрөл T тул str. name-ийн төрөл str.
Алхам 3 (first([95, 88])). Шинэ дуудлага, T шинээр тодорхойлогдоно. list[int]-ийг list[T]-тэй тулгана: T = int. Энэ дуудлагад: T → int. score-ийн төрөл int.
Алхам 4 (name.upper()). name бол str, str-д upper байгаа, зөвшөөрнө.
Алхам 5 (score.upper() гэж бичсэн бол). score бол int, int-д upper байхгүй. mypy програм ажиллахаас өмнө алдаа мэдээлнэ.
Хичээл 4.1-ийн closure-тэй ижил төстэй санаа: T бол үүргэвч шиг дуудлага бүрт өөрийн утгатай "хайрцаг". Ялгаа нь энэ хайрцаг ажиллах үед биш, зөвхөн type checker-ийн шинжилгээнд оршдог. Энэ бол TypeVar-ийн бүх утга: дуудлага бүрийн хувьд оролт ба гаралтын хоорондох холбоог хадгалах.
Хуучин бичлэг
Python 3.12-оос өмнө ижил функцийг ингэж бичдэг байсан бөгөөд одоо ч олон кодонд харагдана:
python
from typing import TypeVar
T = TypeVar("T")
def first(items: list[T]) -> T:
return items[0]TypeVar объектыг модулийн түвшинд тусад нь үүсгэж, нэрийг нь string болгон давтан өгнө. Дараа нь функцийн гарын үсэгт хэрэглэнэ. Утга нь яг ижил. Шинэ бичлэг хоёр давуу талтай: TypeVar тусдаа мөрөнд, функцээс хол тодорхойлогдохгүй, мөн T-ийн хүрээ тодорхой: тэр зөвхөн энэ функцэд хамаарна. Хэрэв таны Python 3.11 эсвэл түүнээс хуучин бол хуучин бичлэгийг хэрэглэнэ.
Одоо class. Stack буюу "овоолго" бол элементүүдийг дээр нь нэмж, дээрээс нь авдаг энгийн өгөгдлийн бүтэц: таваг угааж байгаа хүний таваг овоолго шиг. Сүүлд нэмсэн нь эхэлж гарна. Хамгийн энгийн хувилбараас эхэлье:
python
class Stack[T]:
def __init__(self) -> None:
self._items: list[T] = []class-ийн нэрийн ард [T] бичигдсэн нь "энэ class нь T төрлийн хувьсагчтай generic" гэсэн утгатай. _items attribute-ийн hint list[T]: доторх жагсаалт T төрлийн элементүүдтэй. __init__-ийн буцаах hint -> None нь Python-ий уламжлал: __init__ юу ч буцаадаггүй.
Push method нэмье:
python
def push(self, item: T) -> None:
self._items.append(item)push T төрлийн элемент хүлээн авна. Энэ нь "энэ овоолгын төрлийн элемент л нэмэгдэнэ" гэсэн амлалт. Одоо pop:
python
def pop(self) -> T:
if not self._items:
raise IndexError("Стек хоосон байна")
return self._items.pop()pop T буцаана: нэмсэн төрлөөрөө гарна. Хоосон овоолгоос авах гэвэл ойлгомжтой мессежтэй IndexError шиднэ, Хичээл 8.2-ын зөвлөмжийн дагуу IndexError-ийн төрлийг хадгалсан. Дараа нь peek буюу "дээд элементийг авалгүйгээр харах":
python
def peek(self) -> T | None:
return self._items[-1] if self._items else NoneӨмнөх хичээлийн Union энд байраа олж байна: T | None. Овоолго хоосон бол None, эс бөгөөс T. Generic ба Union хоорондоо чөлөөтэй нийлдэг. Эцэст нь Бүлэг 8-ын протоколууд:
python
def __len__(self) -> int:
return len(self._items)
def __iter__(self) -> Iterator[T]:
for item in reversed(self._items):
yield item__iter__ нь Хичээл 8.4-ийн generator хэлбэр, дээрээс доош давтана. Түүний буцаах hint Iterator[T] нь “T төрлийн элементүүдийг нэг нэгээр өгдөг iterator”: Бүлэг 1-ийн түгээгч, одоо төрлөө зарласан. Iterator-ийг collections.abc модулиас импортлоно.
Бүрэн class ба хэрэглээ:
python
from collections.abc import Iterator
class Stack[T]:
def __init__(self) -> None:
self._items: list[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
if not self._items:
raise IndexError("Стек хоосон байна")
return self._items.pop()
def peek(self) -> T | None:
return self._items[-1] if self._items else None
def __len__(self) -> int:
return len(self._items)
def __iter__(self) -> Iterator[T]:
for item in reversed(self._items):
yield item
history: Stack[str] = Stack()
history.push("/courses")
history.push("/courses/python")
history.push("/courses/python/lesson-3")
print(len(history))
print(history.pop())
print(history.peek())
print(list(history))3
/courses/python/lesson-3
/courses/python
['/courses/python', '/courses']Вэб хөтчийн "буцах" товчны түүх: хуудас бүр дээр нь нэмэгдэж, буцах үед сүүлийнх нь гарна. history: Stack[str] = Stack() мөрөнд шүүгээнд шошго наасан: энэ овоолго str хадгална. Одоо mypy history-тэй холбоотой бүх мөрийг шалгана: history.push(42) гэвэл “str хүлээсэн, int ирлээ” гэж анхааруулна, history.pop()-ийн үр дүнг str гэж мэднэ, тэгэхээр .upper() зөвшөөрнө. Хэрэв Stack[Song] гэвэл Хичээл 8.4-ийн дуунуудын овоолго болно, pop() нь Song буцаана. Нэг class, тоо томшгүй олон тодорхой төрөл.
Trace: шошго ажиллах үед
Анхны жишээний сургамжийг Stack дээр шалгая: generic шошго ажиллах үед хамгаалалт өгдөг үү?
python
numbers: Stack[int] = Stack()
numbers.push(10)
numbers.push("хорь")
print(list(numbers))
print(type(numbers))['хорь', 10]
<class '__main__.Stack'>Алхам 1 (Stack() дуудагдах). Ажиллах үед энгийн Stack объект үүснэ. Stack[int] шошго зөвхөн numbers нэрийн hint-д, __annotations__-д л бичигдсэн. Объектод: _items = [], T-ийн тухай ямар ч мэдээлэл байхгүй.
Алхам 2 (push(10), push("хорь")). Хоёулаа энгийн append. Python string-ийг эсэргүүцэхгүй. Объектод: _items = [10, "хорь"].
Алхам 3 (type(numbers)). Энгийн Stack, Stack[int] биш.
Тэгэхээр шүүгээний шошго нь хаалганы түгжээ биш, зүгээр л бичиг. Хэн ч шошгыг үл тоомсорлож юу ч хийж чадна. Шошгыг уншиж, зөрчлийг мэдээлдэг хүн нь mypy: тэр numbers.push("хорь") мөрийг ажиллуулахаас өмнө илрүүлнэ. Энэ нь өмнөх хичээлийн гол санааг generic-д хэрэглэсэн хэлбэр.
Жишээ 1: хэд хэдэн төрлийн хувьсагч
Нэг функц хэд хэдэн өөр TypeVar-тай байж болно. Dict-ийн түлхүүр ба утгыг сольдог функц:
python
def invert[K, V](mapping: dict[K, V]) -> dict[V, K]:
return {value: key for key, value in mapping.items()}
codes = {"УБ": "Улаанбаатар", "ДА": "Дархан"}
names = invert(codes)
print(names)
print(names["Дархан"]){'Улаанбаатар': 'УБ', 'Дархан': 'ДА'}
ДАK ба V хоёр бие даасан хувьсагч, уламжлалт нэр нь “key” ба “value”-ийн эхний үсэг. Гарын үсэг амлалтыг бүрэн илэрхийлж байна: “K-ээс V рүү dict хүлээн авч, V-ээс K рүү dict буцаана”. mypy codes-ийг харж K = str, V = str гэж дүгнэнэ. Хэрэв бид {1: "нэг"}-ийг өгвөл K = int, V = str, үр дүн нь dict[str, int] болно. Нэг шударга тэмдэглэл: энэ функц V төрлийн утгууд dict-ийн түлхүүр болж чадна гэж таамаглаж байна, өөрөөр хэлбэл hash хийгдэх боломжтой. Утгууд list бол ажиллах үед TypeError гарна. Дараагийн жишээ ийм нөхцлийг hint-д хэрхэн бичихийг харуулна.
Жишээ 2: хязгаарлалт — "ямар ч төрөл, гэхдээ ийм чадвартай"
Заримдаа T бүрэн дурын байж болохгүй. Жагсаалтаас хамгийн урт элементийг олох функцийг төсөөлье: тэр элементүүдэд len() хэрэглэнэ. Хэрэв T дурын бол mypy “T-д len() байгаа эсэх тодорхойгүй” гэж эсэргүүцнэ. Шийдэл нь T-д хязгаарлалт, англиар bound, өгөх:
python
from collections.abc import Iterable, Sized
def longest[T: Sized](items: Iterable[T]) -> T:
return max(items, key=len)
print(longest(["Бат", "Тэмүүлэн", "Сараа"]))
print(longest([[1, 2], [3, 4, 5], []]))Тэмүүлэн
[3, 4, 5]T: Sized нь “T ямар ч төрөл байж болно, гэхдээ Sized байх ёстой” гэсэн утгатай. Sized бол collections.abc модулийн тусгай төрөл, “__len__ method-той аливаа зүйл” гэсэн утгатай. Энэ нь Хичээл 8.2-ын container протоколын нэг заалт. str, list, dict, Хичээл 8.2-ын Playlist бүгд Sized, учир нь бүгд __len__-тэй, хэдийгээр хэн нь ч Sized-ээс удамшаагүй ч. Иймээс longest Playlist-уудын жагсаалттай ч ажиллаж, хамгийн олон дуутайг буцаана, буцаах төрөл нь Playlist хэвээр байна.
Эндээс хоёр шинэ hint харагдаж байна. Iterable[T] нь “T төрлийн элементүүдийг давтаж болох аливаа зүйл”: list, tuple, generator, Playlist, Бүлэг 1-ийн бүх хайрцаг. list[T]-ээс илүү уян хатан: функц зөвхөн давтдаг бол list шаардах шаардлагагүй. Энэ нь hint бичих сайн дадал: функцийн хэрэглэдэг чадварыг л шаард, илүүг биш. Хоёрдугаарт Sized: "энэ method байвал хангалттай" гэсэн төрөл. Энэ санаа, удамшил биш, чадвараар таних, Бүлэг 8-ын duck typing юм. Дараагийн хичээлд бид ийм "чадварын төрлийг" өөрсдөө тодорхойлох аргыг сурна: Protocol.
Хуучин бичлэгт хязгаарлалтыг T = TypeVar("T", bound=Sized) гэж бичдэг.
Жишээ 3: Any, object, T — гурван "юу ч"
Эцэст нь гурван төстэй мэт санагдах hint-ийн ялгааг тодорхой болгоё. Эдгээрийг андуурах нь түгээмэл.
Any бол "шалгахаа больж бай". Any төрлийн утга дээр юу ч хийж болно, mypy юу ч хэлэхгүй. Энэ бол type checker-ээс зугтах гарц: заримдаа хэрэгтэй, жишээлбэл hint-гүй хуучин сантай ажиллах үед, гэвч хэрэглэх бүрт хамгаалалт алдагдана.
object бол "юу ч хүлээн авна, гэхдээ ердөө объект гэж үзнэ". Python-д бүх зүйл object тул object hint-тэй параметр юу ч хүлээн авна. Гэвч mypy object-д зөвхөн бүх объектод байдаг үйлдлүүд, тухайлбал хэвлэх, == харьцуулах, зөвшөөрнө. .upper() дуудвал анхааруулна. Энэ бол "би энэ утгын тухай юу ч мэдэхгүй, бас юу ч хийхгүй" гэсэн шударга мэдэгдэл. Жишээлбэл log-д дурын утгыг хэвлэдэг функцэд тохирно.
T бол "юу ч хүлээн авна, мөн тэр төрлийг санаж, гаралттай холбоно". Generic функц оролтын тухай мэдээллийг алдахгүй, гаралтдаа дамжуулна.
Дүрэм: утгыг ердөө дамжуулж эсвэл хадгалж, буцааж өгдөг бол T. Хүлээн авсан утгаар бараг юу ч хийхгүй, буцаахгүй бол object. Any-г зөвхөн өөр арга байхгүй үед, мөн аль болох жижиг хүрээнд.
Хүсвэл Stack-д __contains__ нэмж, Хичээл 8.2-ын загвараар "/courses" in history гэж асуух боломжтой болгоорой. Түүний item параметрт ямар hint тохиромжтой вэ: T уу, object уу? Python-ий бэлэн list-ийн __contains__ object хүлээн авдаг нь сонирхолтой сэжүүр. Сонирхвол last[T](items: Sequence[T]) -> T | None функц бичиж, хоосон жагсаалтад None буцаадаг болгож болно. Sequence нь collections.abc-д бий, индексээр хандаж болох бүх зүйл. Мөн Хичээл 9.3-ын LOADERS бүртгэлийг generic Registry[T] class болгож, Registry[Callable[..., int]] эсвэл class-уудын бүртгэлд хэрэглэх нь энэ хичээлийг өмнөх бүлэгтэй холбосон туршилт. Эдгээр бүгд санал бөгөөд дараагийн хичээлүүд үүнийг хийсэн гэж үзэхгүй.
Буруу төрлийн элемент чимээгүй нэмэгдэх. Мессежгүй, Trace-ийн сургамж. Stack[int] гэж шошголсон овоолгод string нэмэхэд ажиллах үед юу ч гарахгүй. Шошго бол mypy-д зориулсан мэдээлэл. Хэрэв ажиллах үед хамгаалалт заавал хэрэгтэй бол push дотор isinstance шалгалт нэмэх ёстой, гэвч T ажиллах үед байхгүй тул ердөө generic class-д ингэж шалгах боломжгүй. Ихэнх тохиолдолд зөв шийдэл бол mypy-г төслийн ажлын урсгалд оруулах, Хичээл 10.4-ийн сэдэв.
SyntaxError: invalid syntax. "Синтаксийн алдаа" гэсэн утгатай. class Stack[T]: эсвэл def first[T](...) бичлэгийг Python 3.11 эсвэл түүнээс хуучин хувилбарт ажиллуулахад гарна. Хуучин хувилбарууд нэрийн ард дөрвөлжин хаалтыг танихгүй. Python-ийн хувилбараа python --version-оор шалга. Хэрэв шинэчлэх боломжгүй бол хичээлд үзүүлсэн хуучин бичлэгийг хэрэглэ: T = TypeVar("T") ба class Stack(Generic[T]):.
NameError: name ‘T’ is not defined. “T гэдэг нэр тодорхойлогдоогүй” гэсэн утгатай. Функцийн нэрийн ард [T] мэдэгдэлгүйгээр гарын үсэгт T хэрэглэсэн үед гарч болно: def first(items: list[T]) -> T:. Python 3.12 ба 3.13 энэ алдааг функц тодорхойлогдох үед өгнө. Python 3.14-өөс хойш hint-үүдийг хэрэгтэй үед л боддог тул алдаа шууд гарахгүй, харин mypy эсвэл hint-ийг уншдаг сан хэрэглэх үед илэрнэ. Аль ч тохиолдолд шийдэл нэг: T-г [T]-ээр эсвэл TypeVar-аар зарлах.
TypeError: type ‘Playlist’ is not subscriptable. “Playlist төрөлд дөрвөлжин хаалтаар хандах боломжгүй” гэсэн утгатай. Python-ий хувилбараас хамааран “‘type’ object is not subscriptable” гэж ч бичигдэж болно. Generic биш class-д төрлийн параметр өгөхөд гарна: Playlist[Song]. Playlist-ийг class Playlist: гэж тодорхойлсон тул тэр төрлийн хувьсагчгүй. Хэрэв тэр generic байх ёстой бол class Playlist[T]: гэж зарлана.
IndexError: Стек хоосон байна. Энэ бол бидний мессеж: хоосон овоолгоос pop хийсэн. pop-ийн гарын үсэг -> T гэж хэлдэг тул тэр үргэлж T буцаах ёстой, None буцаах эрхгүй. Тиймээс хоосон үед алдаа шиднэ. Хэрэв "магадгүй байхгүй" гэсэн хариу хүсвэл peek шиг T | None буцаадаг тусдаа method хэрэглэ. Гарын үсэг ба зан чанар нийцтэй байх нь hint-ийн итгэлцлийн үндэс.
Any төрөл type checker-ийг унтраадаг тул оролт ба гаралтын хоорондох холбоог алддаг. TypeVar бол дуудлага бүрт тодорхой төрлөөр солигддог орлогч бөгөөд Python 3.12+-д функц эсвэл class-ийн нэрийн ард [T] гэж зарлагдана. Хуучин бичлэг нь T = TypeVar("T") ба Generic[T]. Type checker дуудлага бүрт argument-уудаас T-г дүгнэж, буцаах төрөлд дамжуулдаг: "юу оруулна, тэр л гарна". Generic class нь Stack[str] шиг шошготойгоор хэрэглэгдэж, түүний бүх method-ууд тэр төрлөөр шалгагдана. Шошго ажиллах үед хамгаалалт өгдөггүй, mypy түүнийг шалгана. Хэд хэдэн бие даасан TypeVar, жишээлбэл K ба V, байж болно. T: Sized шиг хязгаарлалт "ямар ч төрөл, гэхдээ ийм чадвартай" гэсэн нөхцөлийг илэрхийлдэг. Параметрт Iterable шиг хамгийн бага шаардлагатай төрлийг сонгох нь сайн дадал. Any нь шалгалтгүй, object нь юу ч хүлээн авдаг ч бага зүйл зөвшөөрдөг, T нь төрлийг санаж дамжуулдаг.
Энэ хичээлийн Жишээ 2-т Sized гэдэг сонирхолтой төрөлтэй танилцсан: удамшлаар биш, __len__ method байгаа эсэхээр нь таньдаг төрөл. Энэ бол Бүлэг 8-ын duck typing-ийн type hint хэлбэр. Дараагийн хичээлд ийм төрлийг өөрсдөө тодорхойлж сурна: Protocol. "Нугас шиг алхаж, нугас шиг дуугарвал түүнийг нугас гэж үзнэ" гэдгийг гэрээ болгон бичих, mypy тэр гэрээг шалгах. Хичээл 9.2-ын стратегиуд, 9.3-ын loader-ууд, 9.4-ийн сонсогчид, бүгд удамшлын шатлалгүйгээр ижил гэрээг дагадаг байсан. Protocol тэр гэрээг албан ёсны, шалгагдах хэлбэрт оруулна.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.