Ачааллж байна...
Өмнөх хичээлд str | None гэсэн бичлэг хоёр удаа гарч ирсэн бөгөөд би түүнийг "дараа тайлбарлана" гэж хойшлуулсан. Одоо тэр цаг болжээ.
Асуудал нь бодит амьдралаас урган гардаг: заримдаа утга байдаггүй.
Та харилцагчийн дэвтрээс нэр хайлаа — олдсон эсвэл олдоогүй. Та хэрэглэгчийн утасны дугаарыг асуулаа — тэр өгсөн эсвэл өгөөгүй. Та жагсаалтаас хамгийн үнэтэй барааг хайлаа — жагсаалт хоосон байж болно. Та интернэтээс өгөгдөл татлаа (Бүлэг 11) — сервер хариу өгсөн эсвэл өгөөгүй.
Эдгээр бүх тохиолдолд функц нь "энд байна" гэж хариулж чадна, эсвэл "юу ч алга" гэж хариулах ёстой. Python-д тэр "юу ч алга" гэсэн хариултыг илэрхийлдэг тусгай утга нь None юм — Курс 1-ээс танил.
Энэ хичээлд бид None-ыг гүнзгий үзэж, түүнийг type hint-д хэрхэн илэрхийлэх (str | None), болон түүнтэй хэрхэн АЮУЛГҮЙ ажиллахыг сурна. Мөн Python-ы хамгийн түгээмэл ажиллах үеийн алдаануудын нэг болох AttributeError: 'NoneType' object has no attribute ... -ыг задлан тайлбарлана — та түүнийг заавал харах болно, тиймээс урьдчилан бэлдье.
Шошготой хайрцгийн зүйрлэлийг үргэлжлүүлье. Танд "утасны дугаар" гэсэн шошготой хайрцаг байна. Дотор нь юу байна вэ?
Хоёр боломж бий. Нэгдүгээрт, дотор нь цаас байж болно, дээр нь дугаар бичсэн. Хоёрдугаарт, хайрцаг ХООСОН байж болно.
Энд чухал нарийвчлал байна: хоосон хайрцаг нь хайрцаг байхгүйтэй адилгүй. Хайрцаг оршин байна, шошготой, тавиур дээр байна — зөвхөн дотор нь юу ч алга. Энэ бол утга учиртай мэдээлэл: "бид энэ хүний утасны дугаарыг АСУУСАН, гэхдээ тэр өгөөгүй."
None бол яг тэр хоосон хайрцаг. Тэр нь "утга байхгүй" гэдгийг илэрхийлдэг БОДИТ утга юм. Тиймээс None нь 0-тэй ч, хоосон string-тэй ч, False-тэй ч ижил биш:
0 — "утга нь тэг байна" (жишээ нь үнэгүй бараа)
"" — "утга нь хоосон текст байна"
False — "утга нь худал байна"
None — "утга ОГТ байхгүй"
Энэ ялгаа нь нарийн боловч маш чухал. Хэрэв та "хэрэглэгчийн нас" гэсэн талбарт 0 бичвэл тэр нь "энэ хүн нярай" гэсэн үг. Хэрэв None бол "бид түүний насыг мэдэхгүй."
Курс 1-ээс None -ыг товч сануулъя. Тэр нь гурван замаар үүсдэг.
1. Функц return бичээгүй бол:
python
def mendchilekh(ner):
print(f"Сайн байна уу, {ner}!")
ur_dun = mendchilekh("Болд")
print(ur_dun)Сайн байна уу, Болд!
Nonemendchilekh нь зөвхөн хэвлэдэг, юу ч буцаадаггүй. Тиймээс Python нь автоматаар None буцаасан. Яг тиймээс ийм функцийн hint нь -> None байдаг.
2. Функц зориудаар None буцаах:
python
def khaikh(devter, ner):
if ner in devter:
return devter[ner]
return NoneЭнд "олдсонгүй" гэсэн хариултыг None -оор илэрхийлж байна.
3. Хувьсагчид зориудаар None олгох:
python
utas = None"Одоохондоо утга байхгүй, дараа нь орж ирнэ" гэсэн санаа.
Одоо гол сэдэв рүү орно. Дараах функцийг хараарай:
python
def khaikh(devter: dict[str, str], ner: str) -> str:
if ner in devter:
return devter[ner]
return NoneЭнэ hint нь ХУДАЛ. Функц нь -> str гэж амласан ч заримдаа None буцааж байна. Өмнөх хичээлээс санаж байгаа байх: худал hint нь hint байхгүйгээс дор.
Зөв hint:
python
def khaikh(devter: dict[str, str], ner: str) -> str | None:
if ner in devter:
return devter[ner]
return Nonestr | None гэдэг нь "string ЭСВЭЛ None" гэсэн үг. Босоо зураас (|) нь "эсвэл" гэсэн утгатай бөгөөд түүнийг union гэж нэрлэдэг (нэгдэл).
Одоо гарчиг нь үнэнийг хэлж байна: "дэвтэр ба нэр ав; string буцаа, ЭСВЭЛ юу ч биш буцаа." Кодыг уншиж байгаа хүн шууд мэднэ: энэ функцийн үр дүнг шалгах хэрэгтэй.
Энэ бол | None -ийн жинхэнэ зорилго. Тэр нь зөвхөн баримт бичиг биш — тэр нь АНХААРУУЛГА юм. "Болгоомжтой байгаарай, энд юу ч байхгүй байж болно."
Хэдэн жишээ:
python
def utas_avakh(hun: dict[str, str]) -> str | None:
...
def khamgiin_unetei(unes: dict[str, int]) -> str | None:
...
def nas_avakh(ner: str) -> int | None:
...Бичлэг нь тууштай: төрөл | None. Аль ч төрлийн ард | None нэмж болно.
Та бусдын кодод дараах бичлэгийг харах магадлалтай:
python
from typing import Optional
def khaikh(devter: dict[str, str], ner: str) -> Optional[str]:
...Optional[str] нь str | None -тай ЯГ ИЖИЛ утгатай. Хоёулаа "string эсвэл None" гэсэн үг.
Тэгвэл яагаад хоёр бичлэг байна вэ? Түүхэн шалтгаантай. Optional нь хуучин бичлэг бөгөөд typing модулиас import хийх шаардлагатай. Python 3.10-аас эхлэн | бичлэг нэмэгдсэн бөгөөд тэр нь илүү богино, import шаардахгүй.
Энэ курс нь ЗӨВХӨН str | None бичлэгийг ашиглана. Гэхдээ Optional[str] -ийг харвал ойлгож байх ёстой, учир нь олон сан, олон documentation түүнийг ашигласаар байгаа.
Нэг зүйлийг тодруулъя, учир нь энэ нь эхлэгчдийг төөрөгдүүлдэг: Optional гэдэг нь "энэ параметр сонголттой" гэсэн үг БИШ. Тэр нь зөвхөн "утга нь None байж болно" гэсэн үг. Параметрийг сонголттой болгодог зүйл нь default утга (Хичээл 3.1) — тэр нь тэс өөр ойлголт.
Ялгааг харъя:
python
# Заавал, гэхдээ None байж болно
def f(utas: str | None) -> None:
...
f(None) # болно
f("99112233") # болно
f() # АЛДАА — заавал нэг зүйл дамжуулах ёстой
# Сонголттой, default нь None
def g(utas: str | None = None) -> None:
...
g() # болно — default нь None
g("99112233") # болно
g(None) # болноХоёр дахь хэлбэр (утга: str | None = None) нь ХАМГИЙН түгээмэл. Тэр нь "энэ параметр сонголттой, өгөөгүй бол юу ч байхгүй гэж үзнэ" гэсэн санааг илэрхийлдэг. Та түүнийг framework-ийн кодод байнга харах болно.
Санаж байна уу, Хичээл 3.1-д бид def hun_burtgekh(ner, utas=None) гэж бичсэн? Одоо түүнийг бүрэн hint-лэж чадна:
python
def hun_burtgekh(ner: str, utas: str | None = None) -> None:
print(f"Нэр: {ner}")
if utas is None:
print("Утас: бүртгэгдээгүй")
else:
print(f"Утас: {utas}")Гурван ойлголт нэг мөрөнд нийллээ: type hint, union, default утга.
Одоо хамгийн чухал хэсэг рүү орно. | None -ийг зөв бичих нь эхлэл, харин түүнтэй ЗӨВ АЖИЛЛАХ нь илүү чухал.
Дараах кодыг хараарай:
python
def khaikh(devter: dict[str, str], ner: str) -> str | None:
if ner in devter:
return devter[ner]
return None
devter = {"Болд": "99112233", "Сараа": "88445566"}
utas = khaikh(devter, "Төмөр")
print(utas.strip())Traceback (most recent call last):
File "main.py", line 10, in <module>
print(utas.strip())
AttributeError: 'NoneType' object has no attribute 'strip'AttributeError: 'NoneType' object has no attribute 'strip' гэдэг нь "None объектод strip гэсэн method байхгүй байна" гэсэн үг.
Юу болов? khaikh нь "Төмөр" -ийг олоогүй тул None буцаасан. Дараа нь бид None.strip() гэж дуудахыг оролдсон. None нь string биш тул түүнд string-ийн method байхгүй. Программ унасан.
Энэ бол Python-ы хамгийн түгээмэл ажиллах үеийн алдаануудын нэг. Та түүнийг заавал, олон удаа харна. Тиймээс мессежийг сайн танилцъя:
AttributeError — "шинж чанар/method олдсонгүй" гэсэн алдааны төрөл
'NoneType' object — "энэ объект нь None байна" (NoneType бол None-ийн төрлийн нэр)
has no attribute 'strip' — "түүнд strip гэсэн зүйл байхгүй"
Мессежийг уншсанаар та шууд оношилж чадна: "аан, ямар нэг зүйл None болсон байна, тэгээд би түүн дээр method дуудлаа." Дараагийн асуулт: None хаанаас ирсэн бэ? Ихэвчлэн хариулт нь: ямар нэг функц "олдсонгүй" гэж хэлсэн, гэхдээ би сонсоогүй.
Ижил алдааны бусад хувилбарууд:
AttributeError: 'NoneType' object has no attribute 'upper'
AttributeError: 'NoneType' object has no attribute 'append'
AttributeError: 'NoneType' object has no attribute 'items'
AttributeError: 'NoneType' object has no attribute 'json'Сүүлчийнх нь Бүлэг 11-д requests ашиглах үед гарч ирнэ. Бүгд ижил шалтгаантай.
Шийдэл нь маш энгийн: None байж болох утгыг АШИГЛАХААС ӨМНӨ шалга.
python
def khaikh(devter: dict[str, str], ner: str) -> str | None:
if ner in devter:
return devter[ner]
return None
devter = {"Болд": "99112233", "Сараа": "88445566"}
utas = khaikh(devter, "Төмөр")
if utas is None:
print("Утасны дугаар олдсонгүй")
else:
print(utas.strip())Утасны дугаар олдсонгүйПрограмм унасангүй. Бид None -ийг урьдчилан барьж, ойлгомжтой мессеж хэвлэлээ.
is None гэж шалгаж байгааг анзаараарай, == None биш. Энэ бол Python-ы стандарт бичлэг. Хичээл 3.1-д товч дурдсан, одоо дүрэм болгож санаарай: None -ыг шалгахдаа ҮРГЭЛЖ is None эсвэл is not None.
Эсрэг хэлбэр нь ч бас түгээмэл:
python
if utas is not None:
print(utas.strip())
else:
print("Утасны дугаар олдсонгүй")Аль нь илүү тод байхаас хамаарч сонгоно.
Мэргэжлийн кодод илүү түгээмэл байдаг нэг загвар бий: эрт буцах (early return).
python
def utas_khevlekh(devter: dict[str, str], ner: str) -> None:
utas = khaikh(devter, ner)
if utas is None:
print(f"{ner}: олдсонгүй")
return
print(f"{ner}: {utas.strip()}")Энд бид "муу тохиолдол" -ыг эхэнд нь барьж, шууд return хийж байна. Түүний дараах бүх код нь utas нь None БИШ гэдгийг МЭДЭЖ байгаа тул тайван ажиллана.
Энэ загварын давуу тал: код нь баруун тийш үүрлээд явахгүй. Хэрэв гурван зүйл шалгах хэрэгтэй бол if/else нь гурван давхар болж, уншихад хэцүү болно. Эрт буцах нь тэднийг хавтгай байлгана:
python
def bolovsruulakh(devter: dict[str, str], ner: str) -> None:
if not ner:
print("Нэр хоосон байна")
return
utas = khaikh(devter, ner)
if utas is None:
print("Олдсонгүй")
return
print(f"Утас: {utas}")Editor тань энэ загварыг ойлгодог. if utas is None: return гэсэн мөрийн ДАРАА editor нь utas -ыг зөвхөн str гэж үзнэ — тиймээс utas. гэж бичихэд string-ийн method-уудыг санал болгож, None -ийн талаар анхааруулахаа болино. Энэ ухаалаг зан төлөвийг type narrowing гэж нэрлэдэг (төрлийг нарийсгах). Та зөвхөн шалгалт бичих замаар editor-т "энэ цэгээс хойш None байх боломжгүй" гэж хэлж байгаа юм.
Курс 1-д та dictionary[key] гэж хандаж байсан. Хэрэв key байхгүй бол:
python
devter = {"Болд": "99112233"}
print(devter["Төмөр"])Traceback (most recent call last):
File "main.py", line 3, in <module>
print(devter["Төмөр"])
KeyError: 'Төмөр'KeyError гэдэг нь "тийм түлхүүр байхгүй" гэсэн үг. Программ унана.
Гэхдээ Хичээл 3.6-ийн guided build-д бид .get() method ашигласан:
python
devter = {"Болд": "99112233"}
utas = devter.get("Төмөр")
print(utas)None.get() нь key байхгүй бол алдаа гаргахгүй — None буцаана. Тэгэхээр түүний hint нь str | None байна.
Энэ хоёрын ялгааг мэдэх нь чухал:
devter[ner] — key байхгүй бол АЛДАА (KeyError), программ унана.
devter.get(ner) — key байхгүй бол None, программ үргэлжилнэ.
devter.get(ner, "тодорхойгүй") — key байхгүй бол өгсөн default утга.
Гурав дахь хэлбэр нь заримдаа хамгийн тохиромжтой байдаг, учир нь None шалгах шаардлагыг бүрмөсөн арилгадаг:
python
devter: dict[str, str] = {"Болд": "99112233"}
utas = devter.get("Төмөр", "бүртгэгдээгүй")
print(utas.upper())БҮРТГЭГДЭЭГҮЙNone шалгах шаардлагагүй, учир нь utas нь хэзээ ч None болохгүй — түүний hint нь str (union биш).
Практик зөвлөгөө: хэрэв танд боломжийн default утга байгаа бол .get(key, default) ашигла — None -ийн бүх толгойны өвчнөөс сална. Хэрэв "олдоогүй" гэдэг нь жинхэнэ утга учиртай мэдээлэл бол None буцаа, гэхдээ түүнийг шалга.
Ажлын явцад None нь чимээгүй урсаж, хол зайд алдаа үүсгэдэг. Жишээгээр харъя.
python
def khaikh(devter: dict[str, str], ner: str) -> str | None:
return devter.get(ner)
def formatlakh(utas: str) -> str:
return f"+976 {utas}"
devter = {"Болд": "99112233"}
utas = khaikh(devter, "Төмөр")
print(formatlakh(utas))Traceback (most recent call last):
File "main.py", line 13, in <module>
print(formatlakh(utas))
File "main.py", line 6, in formatlakh
return f"+976 {utas}"Түр зогсъё. Энэ тохиолдолд f-string нь None -ийг string болгож хувиргаад ажиллана:
+976 NoneАлдаа гарсангүй! Гэхдээ үр дүн нь утгагүй. "+976 None" гэдэг нь ямар ч утасны дугаар биш.
Энэ бол None -ийн ХАМГИЙН аюултай тал. Заримдаа тэр нь тэсрэлт үүсгэдэг (AttributeError), заримдаа ЧИМЭЭГҮЙ дамжиж, утгагүй үр дүн болж хувирдаг. Хоёр дахь тохиолдол нь илүү муу, учир нь та түүнийг анзаарахгүй.
Зөв шийдэл нь None -ийг АЛЬ БОЛОХ ЭРТ барих:
python
def khaikh(devter: dict[str, str], ner: str) -> str | None:
return devter.get(ner)
def formatlakh(utas: str) -> str:
return f"+976 {utas}"
devter = {"Болд": "99112233"}
utas = khaikh(devter, "Төмөр")
if utas is None:
print("Утасны дугаар олдсонгүй")
else:
print(formatlakh(utas))Утасны дугаар олдсонгүйformatlakh -ийн hint нь utas: str (union БИШ) гэдгийг анзаараарай — тэр функц нь "надад жинхэнэ string өг" гэж шаардаж байна. None шалгалт нь тэр шаардлагыг биелүүлэх үүрэгтэй.
Дүрэм: None -ийг эх сурвалж дээр нь бариарай, түүнийг гүн рүү дамжуулж болохгүй.
Хэрэв сонирхвол: AttributeError: 'NoneType' object has no attribute ... алдааг өөрөө үүсгэж үзээрэй.
python
devter = {"Болд": "99112233"}
utas = devter.get("Төмөр")
print(utas.upper())Алдааг өөрийн нүдээр харсны дараа if utas is None: шалгалт нэмээд, программ эвлэгээр ажиллахыг хараарай. Энэ алдааг та карьерынхаа туршид олон удаа харна — эхний удаагаа зориудаар үүсгэсэн бол дараа нь тэр даруй танина.
Мөн хүсвэл: VS Code дээр if utas is None: return гэж бичээд, түүний ДАРАА utas. гэж бичээд юу санал болгож байгааг хараарай. Editor нь string-ийн method санал болгож, None -ийн талаар анхааруулахаа больсныг мэдрэх болно — type narrowing ажиллаж байна.
1. AttributeError: 'NoneType' object has no attribute ...
Хамгийн түгээмэл. Функц None буцаасан, та түүн дээр method дууджээ. Засвар: if utga is None: гэж шалга, эсвэл .get(key, default) ашигла.
2. == None гэж шалгах
python
if utas == None:
...Ажиллана, гэхдээ буруу зуршил. Зөв: if utas is None:. is нь "яг ижил объект уу" гэж шалгадаг бөгөөд None нь программ дотор ГАНЦХАН удаа оршин байдаг тул is нь үргэлж зөв, үргэлж хурдан.
3. if utas: гэж шалгах (аюултай)
python
def utas_khevlekh(utas: str | None) -> None:
if utas:
print(utas)
else:
print("Байхгүй")
utas_khevlekh("")БайхгүйЭнд алдаа гарлаа. "" (хоосон string) нь None БИШ — тэр бол жинхэнэ утга. Гэвч if utas: нь хоосон string-ийг ч False гэж үздэг тул түүнийг "байхгүй" гэж буруу ангилсан.
0, "", [], {}, False — эдгээр бүгд if x: шалгалтад False болдог. Гэхдээ тэдгээр нь None биш!
Тиймээс: None -ыг шалгахдаа ЗААВАЛ is None бич. if utas: нь өөр асуулт асууж байна ("хоосон биш үү?"), тэр нь заримдаа таны хүссэн зүйл байж болно, гэхдээ андуурч болохгүй.
4. | None бичихээ мартах
python
def khaikh(devter: dict[str, str], ner: str) -> str:
return devter.get(ner)Python нь ажиллуулна, гэхдээ mypy гомдоно:
main.py:2: error: Incompatible return value type (got "str | None", expected "str")Hint нь худал байна — функц заримдаа None буцаадаг. Засвар: -> str | None.
5. Optional-ыг "сонголттой параметр" гэж ойлгох
python
from typing import Optional
def f(utas: Optional[str]) -> None:
...
f()TypeError: f() missing 1 required positional argument: 'utas'Optional нь параметрийг сонголттой БОЛГОДОГГҮЙ. Тэр нь зөвхөн "утга нь None байж болно" гэсэн үг. Сонголттой болгодог зүйл нь default утга: def f(utas: str | None = None).
None нь "утга ОГТ байхгүй" гэдгийг илэрхийлдэг бодит утга. Тэр нь 0, "", False -тэй ИЖИЛ БИШ.
Hint нь төрөл | None: str | None, int | None, list[str] | None.
| None бол зөвхөн баримт бичиг биш — АНХААРУУЛГА: "энэ утгыг шалгах хэрэгтэй."
Хуучин бичлэг Optional[str] нь str | None -тай яг ижил утгатай. Энэ курс шинэ бичлэгийг ашиглана.
Optional нь "сонголттой параметр" гэсэн үг БИШ. Сонголттой болгодог зүйл нь default утга: utas: str | None = None.
None -ыг шалгахдаа ҮРГЭЛЖ is None эсвэл is not None бич. == None биш, if utas: ч биш.
AttributeError: 'NoneType' object has no attribute ... — хамгийн түгээмэл ажиллах үеийн алдаа. Утга нь None болсон, та түүн дээр method дуудсан.
Эрт буцах хэв маяг: if utga is None: return — код нь хавтгай болж, editor нь дараагийн мөрүүдэд type narrowing хийнэ.
devter[key] нь KeyError гаргана; devter.get(key) нь None буцаана; devter.get(key, default) нь default буцаана. Гурав дахь нь ихэнхдээ хамгийн тохиромжтой.
None -ыг эх сурвалж дээр нь бариарай. Түүнийг гүн рүү дамжуулбал чимээгүй, утгагүй үр дүн үүснэ.
Дараагийн хичээлээр Бүлэг 4 дуусна. Тэнд бид бүх зүйлийг нэгтгэж, функцийг БҮРЭН hint-лэх мэргэжлийн зуршлыг тогтооно: параметр бүр, буцаах утга. Мөн би албан ёсоор зарлана — тэр хичээлээс эхлэн энэ курсийн БҮХ код бүрэн hint-тэй байх болно. Курс 1-ийн хоёр танил функцийг бүрэн hint-лэж, ялгааг нь харна.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.