Ачааллж байна...
Та энэ мөрийг хэдэн зуун удаа бичсэн:
python
with open(zam, "r", encoding="utf-8") as f:
aguulga = f.read()Курс 1-ээс эхлэн, файл унших бүрдээ. Бүлэг 6-д JSON бичих бүрдээ. Бүлэг 12.4-д кэш хадгалах бүрдээ.
Гэвч with яг ЮУ хийдэг вэ?
Та түүнийг ХЭРЭГЛЭЖ чадсан. Тэр нь ажилладаг. Гэхдээ тэр нь ямар нэг ИЛБЭ мэт харагддаг — Python-ы нууцлаг түлхүүр үг.
Энэ хичээлд бид түүнийг тайлна.
Мөн та нэг зүйлийг мэдэх болно: with бол try/finally -ийн БОГИНО БИЧЛЭГ юм. Өөр илбэ байхгүй.
Энэ хичээл нь ТАНИХ түвшний хичээл. Бид өөрийн context manager БИЧИХГҮЙ — тэр нь энэ курсийн хамрах хүрээнээс гадуур. Гэхдээ та with -ыг харах бүрдээ "аан, энэ нь ЦЭВЭРЛЭГЭЭГ баталгаажуулж байна" гэж мэдэх болно.
Асуудлыг эхнээс нь харъя.
with -гүйгээр файл унших:
python
f = open("uguudul.txt", "r", encoding="utf-8")
aguulga = f.read()
f.close() # ХААХАА мартаж болохгүй!
print(aguulga)Ажиллана. Гэвч f.close() нь ЗААВАЛ дуудагдах ёстой.
Яагаад чухал вэ?
Шалтгаан 1: файл түгжигдэнэ. Хэрэв та файлыг хаахгүй бол — өөр программ түүнийг нээж чадахгүй байж болно (Windows дээр ялангуяа).
Шалтгаан 2: өгөгдөл алдагдана. Хэрэв та БИЧИЖ байгаа бол — өгөгдөл нь буферт үлдэж, файлд ХҮРЭХГҮЙ байж болно.
python
f = open("garalt.txt", "w", encoding="utf-8")
f.write("Чухал өгөгдөл")
# f.close() мартсан!Файл ХООСОН байж болно. Өгөгдөл алдагдсан.
Шалтгаан 3: нөөц дуусна. Операцийн систем нь нэг дор нээгдэх файлын тоог ХЯЗГААРЛАДАГ. Хэрэв та мянган файл нээгээд хаахгүй бол:
OSError: [Errno 24] Too many open filesХамгийн муу тохиолдол:
python
f = open("uguudul.txt", "r", encoding="utf-8")
aguulga = f.read()
too = int(aguulga) # хэрэв энэ УНАВАЛ?
f.close() # ХЭЗЭЭ Ч дуудагдахгүй!Хэрэв uguudul.txt дотор "тэнэг" гэсэн текст байвал:
Traceback (most recent call last):
File "main.py", line 4, in <module>
too = int(aguulga)
ValueError: invalid literal for int() with base 10: 'тэнэг'Программ УНАСАН. f.close() мөр НЭГ Ч удаа ажиллаагүй.
Файл нээлттэй үлдлээ.
Энэ бол ЖИНХЭНЭ асуудал. Алдаа гарах бүрд нөөц алдагдана.
try/finallyКурс 1-ээс try/except -ыг санаж байгаа байх. Тэнд бас finally гэсэн блок байдаг.
finally нь ҮРГЭЛЖ ажилладаг — алдаа гарсан ч, гараагүй ч.
python
f = open("uguudul.txt", "r", encoding="utf-8")
try:
aguulga = f.read()
too = int(aguulga)
print(too)
finally:
f.close() # ҮРГЭЛЖ ажиллана!
print("Файл хаагдлаа")Хэрэв өгөгдөл зөв бол:
42
Файл хаагдлааХэрэв өгөгдөл буруу бол:
Файл хаагдлаа
Traceback (most recent call last):
...
ValueError: invalid literal for int() with base 10: 'тэнэг'Анзаараарай: "Файл хаагдлаа" нь АЛДААНЫ ӨМНӨ хэвлэгдсэн.
finally нь алдаа гарсан ч ажилласан. Файл хаагдсан. Дараа нь алдаа цааш шидэгдсэн.
Энэ бол ЗӨВ шийдэл.
Одоо тэр кодыг эргэн хараарай:
python
f = open("uguudul.txt", "r", encoding="utf-8")
try:
aguulga = f.read()
too = int(aguulga)
print(too)
finally:
f.close()Долоон мөр. Гэвч жинхэнэ ажил нь зөвхөн ГУРАВ.
try, finally, f.close() — эдгээр нь БҮГД механик. Файл нээх бүрд ижилхэн.
Мөн f.close() -ыг мартаж болно.
Тиймээс Python нь БОГИНО БИЧЛЭГ өгсөн.
with — богино бичлэгpython
with open("uguudul.txt", "r", encoding="utf-8") as f:
aguulga = f.read()
too = int(aguulga)
print(too)Дөрвөн мөр. Ямар ч try, ямар ч finally, ямар ч close().
Гэвч файл нь ҮРГЭЛЖ хаагдана — алдаа гарсан ч, гараагүй ч.
with нь дээрх try/finally кодтой ЯГ ИЖИЛ утгатай.
Батлая:
python
with open("uguudul.txt", "r", encoding="utf-8") as f:
print(f"Файл нээлттэй үү: {not f.closed}")
aguulga = f.read()
print(f"Файл нээлттэй үү: {not f.closed}")Файл нээлттэй үү: True
Файл нээлттэй үү: Falsewith блокоос ГАРМАГЦ файл ХААГДЛАА.
Одоо алдаатай туршъя:
python
try:
with open("uguudul.txt", "r", encoding="utf-8") as f:
aguulga = f.read()
too = int("тэнэг") # ЗОРИУДААР унагана
except ValueError:
print("Алдаа гарлаа")
print(f"Файл хаагдсан уу: {f.closed}")Алдаа гарлаа
Файл хаагдсан уу: TrueАлдаа гарсан ч файл ХААГДСАН.
Тэр л болоо. with бол зөвхөн try/finally.
Ойлголтыг бататгах зүйрлэл.
Ердийн гэрэл (open + close): та өрөөнд ороод унтраалга дарна. Гарахдаа ДАХИН дарж унтраана.
Гэвч мартаж болно. Эсвэл яаран гарч болно. Эсвэл гал гарч, та гүйж гарч болно — гэрэл асаалттай үлдэнэ.
Автомат гэрэл (with): та өрөөнд ороход гэрэл АСНА. Гарахад тэр нь ӨӨРӨӨ УНТАРНА.
Та мартаж чадахгүй. Яаран гарсан ч — гэрэл унтарна. Гал гарсан ч — гэрэл унтарна.
with бол автомат унтраалга. Тэр нь ЦЭВЭРЛЭГЭЭГ баталгаажуулдаг.
Одоо нэр томьёогоо тодруулъя.
with -тэй ажиллаж чаддаг ЯМАР Ч объектыг CONTEXT MANAGER гэж нэрлэдэг.
Context manager болохын тулд объект нь ХОЁР special method -тэй байх ёстой (Бүлэг 8-аас танил):
__enter__ — with блокт ОРОХОД ажиллана.
__exit__ — with блокоос ГАРАХАД ажиллана. Алдаа гарсан ч ажиллана.
Тэр л болоо. ХОЁР method.
Бүлэг 8.3-ын ПРОТОКОЛ дахин ажиллаж байна:
"Протокол гэдэг нь: 'хэрэв та ЭДГЭЭР method-уудтай бол, та ЭНЭ ЗҮЙЛИЙГ хийж чадна.'"
Протокол | Method | Юу боломжтой болох вэ |
|---|---|---|
Урттай байх |
|
|
Хэвлэгдэх |
|
|
Context manager |
|
|
Файл объект нь тэр хоёр method-той. Тиймээс with open(...) ажилладаг.
python
with open("uguudul.txt", "r", encoding="utf-8") as f:
aguulga = f.read()Python нь дотроо ЯГ дараах зүйлийг хийж байна:
python
fail = open("uguudul.txt", "r", encoding="utf-8")
f = fail.__enter__() # блокт ОРЛОО
try:
aguulga = f.read()
finally:
fail.__exit__(None, None, None) # блокоос ГАРЛААГурван зүйлийг анзаараарай.
1. __enter__() нь УТГА буцаана. Тэр утга нь as f -ийн f болно.
Файлын хувьд __enter__ нь ӨӨРИЙГӨӨ буцаадаг. Тиймээс f нь файл объект болно.
2. __exit__() нь finally дотор. Тиймээс ҮРГЭЛЖ ажиллана.
3. Файлын __exit__ нь close() дуудаж байна. Тэр л бүх нууц.
Илбэ БАЙХГҮЙ. Зөвхөн хоёр method ба try/finally.
as f — заавал бишНэг нарийвчлал: as f нь ЗААВАЛ БИШ.
python
with open("garalt.txt", "w", encoding="utf-8"):
print("Файл нээгдлээ") # гэхдээ түүнд хандаж чадахгүйАжиллана, гэхдээ ашиггүй — файлд бичих боломжгүй.
Файлын хувьд as f нь ҮРГЭЛЖ хэрэгтэй.
Гэхдээ зарим context manager нь юу ч буцаадаггүй. Тэдгээрийн хувьд as шаардлагагүй.
Одоо буцаж хараарай. Та with -ыг файлаас ГАДНА ч хэрэглэсэн.
Бүлэг 11.5-аас:
python
with requests.Session() as session:
for lat, lon, ner in khotууd:
hariult = session.get(url, params=..., timeout=10)
...Session нь context manager!
Тэр үед би ингэж хэлсэн:
"
with ... as session:— ажил дуусахад холболтыг автоматаар ХААНА. (withяг юу хийдгийг Бүлэг 13.3-т бүрэн тайлбарлана.)"
Одоо та мэднэ: Session нь __exit__ -тэй бөгөөд тэр нь холболтыг хаадаг.
Хэрэв with ашиглаагүй бол:
python
session = requests.Session()
...
session.close() # МАРТАЖ БОЛНОИжил асуудал. with нь түүнийг шийднэ.
Заримдаа хэд хэдэн зүйлийг зэрэг нээх хэрэгтэй болдог.
Жишээ: нэг файлаас уншиж, нөгөөд бичих.
python
with open("oroolt.txt", "r", encoding="utf-8") as oroolt:
with open("garalt.txt", "w", encoding="utf-8") as garalt:
for mur in oroolt:
garalt.write(mur.upper())Ажиллана. Гэвч үүрлэлт нь муухай.
Илүү сайн: НЭГ with дотор ХОЁУЛАА:
python
with (
open("oroolt.txt", "r", encoding="utf-8") as oroolt,
open("garalt.txt", "w", encoding="utf-8") as garalt,
):
for mur in oroolt:
garalt.write(mur.upper())Таслалаар тусгаарлана. Хоёулаа __exit__ дуудагдана — эсрэг дарааллаар (сүүлчийнх нь эхлээд).
Хуучин бичлэг (Python 3.10-аас өмнө) нь дугуй хаалтгүй байсан:
python
with open("a.txt") as a, open("b.txt") as b:
...Тэр нь одоо ч ажиллана, гэхдээ урт мөр болвол уншихад хэцүү.
__exit__ ба алдааОдоо нэг нарийн зүйлийг тодруулъя.
__exit__ нь ГУРВАН аргумент авдаг:
python
def __exit__(self, alda_turul, alda_utga, traceback):
...Хэрэв алдаа ГАРААГҮЙ бол — гурвуулаа None.
Хэрэв алдаа ГАРСАН бол — тэдгээр нь алдааны мэдээллийг агуулна.
Тиймээс context manager нь алдаа гарсан эсэхийг МЭДЭЖ чадна.
Жишээ нь өгөгдлийн сангийн context manager:
python
# Ойролцоо жишээ (энэ курсэд өгөгдлийн сан үзэхгүй)
with database.transaction() as t:
t.execute("INSERT ...")
t.execute("UPDATE ...")Алдаа гараагүй бол → __exit__ нь өөрчлөлтийг ХАДГАЛНА (commit)
Алдаа гарсан бол → __exit__ нь өөрчлөлтийг БУЦААНА (rollback)
Тэр нь маш хүчирхэг. Гэхдээ энэ курсэд өгөгдлийн сан үзэхгүй тул зөвхөн дурдаад өнгөрье.
Мөн __exit__ нь алдааг НУУЖ чадна. Хэрэв тэр нь True буцаавал — алдаа цааш шидэгдэхгүй.
Тэр нь ихэвчлэн МУУ санаа. Алдааг нуух нь Бүлэг 13.2-ын дүрмийг зөрчинө.
Файлын __exit__ нь False (эсвэл None) буцаадаг тул алдаа цааш шидэгддэг. Тэр нь зөв.
with ашиглах вэwith -ыг ЗААВАЛ ашиглах газрууд:
1. Файл. ҮРГЭЛЖ.
python
with open(zam, "r", encoding="utf-8") as f:
...2. requests.Session() (Бүлэг 11.5).
3. Өгөгдлийн сангийн холболт (энэ курсэд үзэхгүй).
4. Түгжээ, thread (энэ курсэд үзэхгүй).
Ерөнхий дүрэм:
Хэрэв ямар нэг зүйлийг НЭЭХ, дараа нь ХААХ хэрэгтэй бол — тэр нь ихэвчлэн context manager юм.
Documentation -оос яаж мэдэх вэ?
Хэрэв documentation -д with -тэй жишээ байвал — тэр нь context manager. Хэрэв тэнд .close() method байвал — with -ыг ашиглаж болох магадлалтай.
Энэ курс нь өөрийн context manager бичихийг ЗААХГҮЙ. Тэр нь ховор хэрэгцээ бөгөөд илүү гүнзгий сэдэв.
Гэхдээ хэрэв та түүнийг бусдын кодод харвал — ойлгож байх ёстой.
Хамгийн энгийн арга нь contextlib.contextmanager decorator:
python
from contextlib import contextmanager
import time
@contextmanager
def khugatsaa_khemjikh(ner: str):
ekhelsen = time.time()
try:
yield # ЭНД with блок ажиллана
finally:
duussan = time.time()
print(f"[{ner}: {duussan - ekhelsen:.2f}с]")
with khugatsaa_khemjikh("Том тооцоолол"):
niilber = sum(i * i for i in range(10_000_000))
print(f"Нийлбэр: {niilber:,}")[Том тооцоолол: 1.34с]
Нийлбэр: 333,333,283,333,335,000,000Танил зүйлс:
@contextmanager — decorator (Бүлэг 10)!
yield — generator (Бүлэг 13.1)!
try/finally — цэвэрлэгээ
yield -ийн ӨМНӨХ код = __enter__yield -ийн ДАРААХ код = __exit__
Бүлэг 10 ба 13.1-ийн мэдлэг нэгдэж байна.
Гэхдээ дахин: энэ курс нь үүнийг ЗААВАЛ шаардахгүй. Зөвхөн таних түвшинд.
Хэрэв сонирхвол: with -гүйгээр файл нээгээд, дунд нь 1/0 гэсэн мөр нэмээд (ZeroDivisionError), программ унасны дараа f.closed -ыг шалгаарай. Файл нээлттэй үлдсэн байх болно.
Дараа нь with ашиглаад ижил зүйлийг хийж, файл ХААГДСАН гэдгийг батлаарай.
Тэр туршилт нь with -ийн бүх утга учрыг харуулна.
Мөн хүсвэл: дээрх khugatsaa_khemjikh context manager -ыг бичээд ажиллуулж үзээрэй. Бүлэг 10-ын decorator ба Бүлэг 13.1-ийн yield нэгдэж байгааг мэдрээрэй.
1. with -гүйгээр файл нээх
python
f = open(zam, "r", encoding="utf-8")
aguulga = f.read()
f.close()Ажиллана, гэхдээ алдаа гарвал файл нээлттэй үлдэнэ. ҮРГЭЛЖ with ашигла.
2. with блокоос ГАДНА файлд хандах
python
with open(zam, "r", encoding="utf-8") as f:
pass
aguulga = f.read() # блокоос ГАДНА!ValueError: I/O operation on closed file.I/O operation on closed file — "хаагдсан файл дээр үйлдэл хийж байна."
Файл нь блокоос гармагц ХААГДСАН. Бүх ажлаа блок ДОТОР хий.
3. Агуулгыг блок дотор УНШИЖ, гадна нь АШИГЛАХ (энэ нь ЗӨВ!)
python
with open(zam, "r", encoding="utf-8") as f:
aguulga = f.read() # блок ДОТОР уншсан
print(aguulga) # блок ГАДНА ашиглаж БОЛНОЭнэ бол ЗӨВ. aguulga нь ердийн string — файлтай холбоогүй.
Ялгааг ойлгоорой: ФАЙЛ ОБЪЕКТ нь хаагдана, гэхдээ түүнээс УНШСАН ӨГӨГДӨЛ нь хэвээрээ.
4. close() -ыг with дотор дуудах
python
with open(zam, "r", encoding="utf-8") as f:
aguulga = f.read()
f.close() # ХЭРЭГГҮЙ!Ажиллана (close() -ыг хоёр удаа дуудахад алдаа гарахгүй), гэхдээ утгагүй. with нь түүнийг өөрөө хийнэ.
5. Олон файлыг үүрлэх
python
with open("a.txt") as a:
with open("b.txt") as b:
with open("c.txt") as c:
...Уншихад хэцүү. Нэг with дотор таслалаар тусгаарла.
with бол try/finally -ийн БОГИНО БИЧЛЭГ. Илбэ БАЙХГҮЙ.
Асуудал: ЦЭВЭРЛЭГЭЭ. Файл нээвэл — ХААХ ёстой. Гэвч алдаа гарвал close() мөр ХЭЗЭЭ Ч ажиллахгүй → файл нээлттэй үлдэнэ, өгөгдөл алдагдана, нөөц дуусна.
finally нь ҮРГЭЛЖ ажилладаг — алдаа гарсан ч, гараагүй ч.
with нь тэр try/finally -г таны өмнөөс бичдэг.
python
with open(zam, "r", encoding="utf-8") as f: # 1 мөр
...оронд нь:
python
f = open(zam, "r", encoding="utf-8") # 5 мөр
try:
...
finally:
f.close()Зүйрлэл: автомат унтардаг гэрэл. Гарахад ӨӨРӨӨ унтарна — та мартаж чадахгүй.
CONTEXT MANAGER = __enter__ ба __exit__ method-той объект (Бүлэг 8.3-ын ПРОТОКОЛ).
__enter__ — блокт ОРОХОД. Утга буцаана → as f
__exit__ — блокоос ГАРАХАД. Алдаа гарсан ч ажиллана. Файлын хувьд close() дуудна.
Та үүнийг АЛЬ ХЭДИЙН ашигласан: open(), requests.Session() (Бүлэг 11.5).
Олон context manager:
python
with (
open("a.txt") as a,
open("b.txt") as b,
):Ерөнхий дүрэм: НЭЭХ + ХААХ хэрэгтэй бол → context manager.
with блокоос ГАДНА файлд хандаж болохгүй → ValueError: I/O operation on closed file. Гэхдээ УНШСАН ӨГӨГДӨЛ нь хэвээрээ.
Өөрийн context manager: @contextmanager + yield (Бүлэг 10 + 13.1 нэгдэж байна). Энэ курс шаардахгүй, зөвхөн таних.
Дараагийн хичээлээр Бүлэг 13 дуусна. Тэнд бид iterator -ыг үзнэ.
Асуулт нь: for loop нь ЯГ ЮУ хийдэг вэ?
Та түүнийг Курс 1-ээс эхлэн хэдэн мянган удаа бичсэн. Тэр нь string, list, dictionary, set, tuple, файл, generator, range, zip — БҮГД дээр ажилладаг.
Тэдгээр нь огт өөр төрлүүд. Тэгвэл for яаж бүгд дээр ажиллаж байна вэ?
Хариулт нь Бүлэг 8.3-ын ПРОТОКОЛ бөгөөд Бүлэг 13.1-ийн generator-ыг бүрэн тайлна. Тэр хичээлээр Python-ы хамгийн үндсэн механизмуудын нэг эцэст нь тодорхой болно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.