Ачааллж байна...
Түвшин 4 гурван сэдэвтэй байсан: төрөл, алдаа, хурд. Бүлэг 10-т typing-ийг mypy-тай хамт эзэмшсэн. Бүлэг 11-т exception-ийг санамсаргүй тохиолдол биш, програмын төлөвлөгдсөн хэсэг болгосон: шатлал, raise from, цэвэрлэгээ. Бүлэг 12-т хурдыг таамаглалаас хэмжилт болгосон. Энэ build гурвыг нэг бодит төсөлд нэгтгэнэ, өмнөх түвшний build-үүдийн адил тусдаа Git branch, pytest тест, commit-оор дуусна.
Төсөл нь захиалгын импортлогч: дэлгүүрийн системээс экспортолсон CSV файлыг уншиж, мөр бүрийг шалгаж, зөв захиалгуудыг төрөлжсөн объект болгож, алдаатай мөрүүдийг мөрийн дугаар ба шалтгаантай нь цуглуулж, хураангуй тайланг хагас бичигдэх аюулгүйгээр хадгална. Бодит ажилд ийм импортлогч маш түгээмэл: банкны хуулга, нягтлангийн экспорт, бүртгэлийн жагсаалт бүгд ийм хэлбэрээр системд ордог. Бодит өгөгдөл үргэлж бохир байдаг: буруу тоо, мэдэгдэхгүй утга, давхардсан мөр.
Бид build-ийг Түвшин 4-ийн дарааллаар явуулна. Эхлээд төрлүүд ба exception шатлал. Дараа нь хамгийн шууд хувилбарыг бичиж ажиллуулна, тэр нь удаан болж таарна. cProfile-оор халуун цэгийг олж, засаад, хэмжээг өөрчилж шалгана. Дараа нь хураангуйг Хичээл 11.3-ын atomic_write-ээр бичнэ. Эцэст нь mypy-ийн strict горим, pytest, commit.
Гаалийн шалган нэвтрүүлэх цэгийг төсөөлөөд үзээрэй. Ачааны машинууд ирэх бүрт байцаагч бичиг баримтыг шалгана. Зөв бол нэвтрүүлж, бүртгэлд оруулна. Буруу бол машиныг хажуу тийш гаргаж, яг ямар шалтгаанаар буцаасныг тэмдэглэнэ. Хамгийн чухал нь: нэг машины бичиг баримт буруу байснаас болж цэг бүхэлдээ хаагдахгүй. Харин цэгийн өөрийнх нь асуудал, жишээлбэл цахилгаан тасарвал бүх ажил зогсоно. Энэ хоёр төрлийн асуудлыг ялгах нь Бүлэг 11-ийн шатлалын санаа.
Импортлогч бол тэр цэг. Мөр бүр бол машин. Алдаатай мөр бол хажуу тийш гаргасан машин: InvalidRowError, мөрийн дугаар ба шалтгаантай. Файл олдохгүй бол цахилгаан тасарсан: OrderImportError, бүх ажил зогсоно. Бүртгэл бол хураангуй тайлан: тэр нь хагас бичигдэх ёсгүй.
Өмнөх build-үүдийн хавтаст шинэ branch үүсгэ:
git switch -c level4-order-importerШинэ хавтас нээж, generate_orders.py файл үүсгэ. Энэ script Хичээл 3.4-ийн генераторын адил seed-тэй санамсаргүй өгөгдөл үүсгэнэ, гэхдээ санаатайгаар бохир:
python
import csv
import random
import sys
from pathlib import Path
def generate(path: Path, n: int, seed: int = 42) -> None:
rng = random.Random(seed)
statuses = ["paid", "paid", "paid", "pending", "cancelled"]
with path.open("w", encoding="utf-8", newline="") as f:
writer = csv.writer(f)
writer.writerow(["order_id", "customer", "amount", "status"])
for i in range(n):
order_id = f"ORD-{i:06d}"
amount = str(rng.randint(1_000, 200_000))
status = rng.choice(statuses)
roll = rng.random()
if roll < 0.002:
amount = "45к"
elif roll < 0.004:
status = "unknown"
elif roll < 0.006 and i > 0:
order_id = f"ORD-{i - 1:06d}"
customer = f"Хэрэглэгч-{rng.randrange(500)}"
writer.writerow([order_id, customer, amount, status])
if __name__ == "__main__":
n = int(sys.argv[1]) if len(sys.argv) > 1 else 20_000
generate(Path("orders.csv"), n)Хичээл 6-ын Path объект файлын замыг string-ийн оронд илэрхийлнэ. rng.random() 0-ээс 1 хүртэлх тоо өгөх тул ойролцоогоор мөр бүрийн 0.2 хувьд буруу дүн, 0.2 хувьд мэдэгдэхгүй төлөв, 0.2 хувьд өмнөх мөрийн дугаарыг давтсан захиалга орно. newline="" нь csv модулийн шаардлага, Түгээмэл алдаанууд хэсэгт яагаад гэдгийг харна. sys.argv нь командын мөрийн аргументууд: python generate_orders.py 10000 гэвэл 10,000 мөр үүсгэнэ, аргументгүй бол 20,000.
python generate_orders.pyimporter.py файл үүсгэ. Эхлээд импортууд, төрлүүд ба exception-ууд:
python
import csv
import os
from collections.abc import Iterator
from contextlib import contextmanager
from dataclasses import dataclass, field
from pathlib import Path
from typing import Literal, TextIO, get_args
Status = Literal["paid", "pending", "cancelled"]
STATUS_BY_NAME: dict[str, Status] = {name: name for name in get_args(Status)}
class OrderImportError(Exception):
"""Захиалга импортлох үеийн бүх алдааны үндэс."""
class InvalidRowError(OrderImportError):
"""Нэг мөр буруу. Импорт үргэлжилнэ, мөр алгасагдана."""
def __init__(self, line: int, reason: str) -> None:
super().__init__(f"{line}-р мөр: {reason}")
self.line = line
self.reason = reason
class DuplicateOrderError(InvalidRowError):
"""Захиалгын дугаар өмнө нь орсон."""
def __init__(self, line: int, order_id: str) -> None:
super().__init__(line, f"давхардсан захиалга {order_id}")
self.order_id = order_id
@dataclass(frozen=True)
class Order:
order_id: str
customer: str
amount: int
status: Status
@dataclass
class ImportResult:
orders: list[Order] = field(default_factory=list)
errors: list[InvalidRowError] = field(default_factory=list)Status бол Хичээл 10.1-ийн Literal төрөл: зөвхөн гурван утга. STATUS_BY_NAME нь string-ийг Status төрөлд хөрвүүлэх dict. get_args(Status) гурван утгыг өгөх ба dict comprehension тус бүрийг өөртэй нь холбоно. Яагаад зүгээр if status in ... шалгаад хэрэглэж болохгүй гэж? mypy нь шалгалтын дараа ч string-ийг Status гэж ойлгодоггүй. Түгээмэл алдаанууд хэсэгт тэр алдааны мессежийг харна. dict-ээс хайх нь шалгалт ба хөрвүүлэлтийг нэг алхамд хийж, mypy-д зөв төрлийг өгнө.
Exception шатлал Хичээл 11.1-ийн загвараар: OrderImportError бүх алдааны үндэс. InvalidRowError нэг мөрийн асуудал: line ба reason attribute-уудтай тул алдааг барьсан код мэдээллийг string задлахгүйгээр авна. DuplicateOrderError нь InvalidRowError-ийн хүүхэд: давхардал бол буруу мөрийн тусгай тохиолдол, тиймээс except InvalidRowError түүнийг ч барина. Docstring бүр алдааны утгыг хэлдэг: импорт үргэлжлэх үү, зогсох уу.
Order бол Хичээл 7.4-ийн frozen dataclass: импортлогдсон захиалгыг санамсаргүй өөрчлөхөөс хамгаална. ImportResult-ийн жагсаалтууд default_factory-тэй, Хичээл 7.4-ийн урхинаас зайлсхийж.
Одоо мөр задлах ба импортлох функцүүд. Бид эхлээд хамгийн шууд хувилбарыг бичнэ:
python
def parse_row(line: int, row: dict[str, str]) -> Order:
try:
amount = int(row["amount"])
except (ValueError, TypeError) as error:
raise InvalidRowError(line, f"дүн бүхэл тоо биш: {row['amount']!r}") from error
status = STATUS_BY_NAME.get(row["status"])
if status is None:
raise InvalidRowError(line, f"үл мэдэгдэх төлөв: {row['status']!r}")
return Order(row["order_id"], row["customer"], amount, status)
def import_orders(path: Path) -> ImportResult:
try:
f = path.open(encoding="utf-8", newline="")
except FileNotFoundError as error:
raise OrderImportError(f"файл олдсонгүй: {path}") from error
result = ImportResult()
seen: list[str] = []
with f:
reader = csv.DictReader(f)
for row in reader:
line = reader.line_num
try:
order = parse_row(line, row)
if order.order_id in seen:
raise DuplicateOrderError(line, order.order_id)
except InvalidRowError as error:
result.errors.append(error)
else:
seen.append(order.order_id)
result.orders.append(order)
return resultТүвшин 4-ийн бүх хэрэгсэл энэ хоёр функцэд бий. parse_row-д int() ValueError шидвэл Хичээл 11.2-ын raise from түүнийг InvalidRowError болгон, анхны алдааг __cause__-д хадгална. TypeError нь мөрөнд багана дутуу байх тохиолдолд: csv.DictReader дутуу утгыг None болгодог бөгөөд int(None) TypeError шиднэ.
import_orders-д хоёр төрлийн алдаа ялгагдана. Файл олдохгүй бол OrderImportError шидэгдэж импорт бүхэлдээ зогсоно: гаалийн цэгийн цахилгаан тасрах. Файлыг try дотор нээж, with-ийг дараа нь бичсэн нь Хичээл 11.3-ын нарийн try зарчим: зөвхөн нээлтийн алдааг барина, унших явцын алдааг биш. Мөрийн алдаа бол InvalidRowError: цуглуулж, үргэлжлүүлнэ.
Давталт доторх try/except/else бол Хичээл 11.3-ын else-ийн жинхэнэ хэрэглээ: захиалга seen ба orders-д зөвхөн амжилттай үед л нэмэгдэнэ, нэмэх код try-ийн хамгаалалтаас гадна. reader.line_num нь файлын физик мөрийн дугаар: толгой мөр 1, анхны өгөгдөл 2. Хэрэв CSV-ийн нэг талбар доторх шинэ мөртэй байвал энэ дугаар enumerate-ээс илүү үнэн.
Одоо main ба ажиллуулалт:
python
def main() -> None:
result = import_orders(Path("orders.csv"))
print(f"{len(result.orders):,} захиалга, {len(result.errors):,} алдаа")
if __name__ == "__main__":
main()python importer.py19,880 захиалга, 120 алдааТоонууд жишээ: seed 42 тул таны гаралт ажиллуулах бүрт ижил, гэвч миний харуулснаас бага зэрэг ялгаатай байж болно. Импорт зөв ажилласан, гэхдээ хэдэн секунд хүлээлгэсэн. 20,000 мөрийн CSV-д хэтэрхий удаан.
Хичээл 12.1-ийн загварын эхний алхам бол таамаглал. Хичээл 12.3-ыг уншсан бол та аль хэдийн сэжиглэж байгаа байх. Гэвч загвар таамаглалыг хэмжилтээр шалгахыг шаарддаг:
python -m cProfile -s tottime importer.pyЭхний мөрүүд, нэг ажиллуулалт:
19,880 захиалга, 120 алдаа
412307 function calls in 2.412 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1 2.203 2.203 2.398 2.398 importer.py:61(import_orders)
20001 0.071 0.000 0.105 0.000 csv.py:179(__next__)
20000 0.038 0.000 0.061 0.000 importer.py:50(parse_row)
...Мөрийн дугаарууд, ялангуяа csv.py-ийнх, таны Python-ий хувилбараас хамаарч өөр байна.
Хүснэгт сонирхолтой зүйл харуулж байна. import_orders-ийн tottime 2.203 секунд, нийт хугацааны 91 хувь. Энэ функц өөрөө бараг юу ч хийдэггүй мэт: давталт, хоёр append. CSV унших (__next__) ба мөр задлах (parse_row) хоёулаа хурдан. Тэгвэл import_orders-ийн өөрийн хугацаа юунд зарцуулагдсан бэ?
Хариулт нь cProfile-ийн нэг хязгаарлалт. Profiler функцийн дуудлагуудыг бүртгэдэг. order.order_id in seen бол функцийн дуудлага биш, тайлбарлагчийн нэг үйлдэл: тиймээс түүний хугацаа тусдаа мөр болж харагдахгүй, түүнийг агуулж буй функцийн tottime-д нэмэгдэнэ. Profiler бидэнд функц хүртэлх нарийвчлалыг өгнө, тэр функцийн дотор юу удаан байгааг кодыг уншиж олно. Хичээл 12.2-т хэлсэн шиг: profiler хаана харахыг заадаг, яагаад гэдгийг код хэлдэг.
import_orders-ийн кодыг уншихад давталт дотор ганц сэжигтэй мөр бий: list-ийн in. Энэ бол Хичээл 12.3-ын unique_with_list-ийн яг ижил загвар: давталт бүр өсөн нэмэгдэж буй жагсаалтыг бүтнээр нь шалгана. Таамаглал профайлаар нарийссан. Одоо засаж батална.
Ганцхан зүйлийг өөрчил, хоёр мөр:
python
seen: set[str] = set()python
seen.add(order.order_id)Эхнийх нь seen: list[str] = []-ийн оронд, хоёр дахь нь seen.append(...)-ийн оронд. Дахин профайллахаас өмнө Хичээл 12.1-ийн тав дахь алхмыг хийе: хэмжээг өөрчилж шалгах. measure.py файл:
python
import time
from pathlib import Path
from generate_orders import generate
from importer import import_orders
for n in (10_000, 20_000, 40_000):
path = Path(f"orders_{n}.csv")
generate(path, n)
start = time.perf_counter()
import_orders(path)
print(f"n={n:>6,}: {time.perf_counter() - start:.3f} с")
path.unlink()Хэмжилт өгөгдөл үүсгэсний дараа эхэлнэ, timeit-ийн setup-ийн адил. path.unlink() туршилтын файлыг устгана. Энэ script-ийг засварын өмнө ба дараа ажиллуулсан нэг компьютер дээрх үр дүн:
Засварын өмнө (list):
n=10,000: 0.523 с
n=20,000: 2.051 с
n=40,000: 8.176 с
Засварын дараа (set):
n=10,000: 0.043 с
n=20,000: 0.085 с
n=40,000: 0.171 сЗасварын өмнө хоёр дахин том өгөгдөл дөрөв дахин удаан: квадрат өсөлт. Засварын дараа хоёр дахин: шугаман. 20,000 мөрт 24 дахин хурдан, 40,000 мөрт 48 дахин, ялгаа өгөгдөл томрох тусам өсөх болно. Дахин профайллавал import_orders-ийн tottime жижгэрч, хугацаа CSV унших ба мөр задлах хоёрт хуваагдсаныг харна: нэг мөр давамгайлахаа больсон, Хичээл 12.2-ын "оновчлол дуусах дохио".
Импортын үр дүнг файлд хадгална. Хичээл 11.3-ын atomic_write-ийг typing-тэй хувилбараар importer.py-д нэм:
python
@contextmanager
def atomic_write(path: Path, encoding: str = "utf-8") -> Iterator[TextIO]:
temp_path = path.with_name(path.name + ".tmp")
try:
with temp_path.open("w", encoding=encoding) as f:
yield f
except BaseException:
temp_path.unlink(missing_ok=True)
raise
else:
os.replace(temp_path, path)
def write_summary(result: ImportResult, path: Path) -> None:
totals: dict[str, int] = {}
for order in result.orders:
totals[order.status] = totals.get(order.status, 0) + order.amount
lines = [f"{status}: {amount:,}₮" for status, amount in sorted(totals.items())]
lines.append(f"Алдаатай мөр: {len(result.errors)}")
lines.extend(str(error) for error in result.errors[:10])
with atomic_write(path) as f:
f.write("\n".join(lines) + "\n")Хичээл 11.3-ын хувилбараас хоёр өөрчлөлт бий. Path объект ашигласан: with_name түр файлын нэрийг үүсгэнэ. unlink(missing_ok=True) нь файл байхгүй бол алдаа гаргахгүй, os.path.exists шалгалт ба os.remove хоёрыг нэг дуудлагаар орлоно: цэвэрлэгээний код өөрөө алдаа гаргахгүй байх дүрэм. Generator context manager-ийн буцаах төрөл Iterator[TextIO]: yield хийгдэх утга текст файл.
write_summary нь Хичээл 12.3-ын join загвар: мөрүүдийг жагсаалтад цуглуулж, нэг join-оор бичнэ. Зөвхөн эхний арван алдааг харуулна: 10,000 алдаатай файлд хураангуй унших боломжтой хэвээр байна. main-д нэг мөр нэм:
python
def main() -> None:
result = import_orders(Path("orders.csv"))
write_summary(result, Path("summary.txt"))
print(f"{len(result.orders):,} захиалга, {len(result.errors):,} алдаа")summary.txt-ийн эхлэл ийм хэлбэртэй:
cancelled: 400,875,312₮
paid: 1,215,027,446₮
pending: 405,118,903₮
Алдаатай мөр: 120
49-р мөр: давхардсан захиалга ORD-000046
...Хичээл 10.4-т бид mypy-ийг тохиргоотой ажиллуулсан. Энэ build-д хамгийн хатуу горимыг хэрэглэнэ. pyproject.toml:
toml
[tool.mypy]
strict = truestrict нь бүх функцийн аргумент ба буцаах утгад төрөл шаардаж, Any-ийн чимээгүй тархалтыг хориглодог олон шалгалтыг нэг дор асаана.
mypy importer.pySuccess: no issues found in 1 source file"Амжилттай: 1 файлд асуудал олдсонгүй." Энэ үр дүн өөрөө амархан ирээгүй: STATUS_BY_NAME dict, Iterator[TextIO], ImportResult-ийн төрөлтэй жагсаалтууд бүгд strict горимд нийцэхийн тулд. Түгээмэл алдаанууд хэсэгт эхний оролдлогын алдааг харна.
test_importer.py файл:
python
from pathlib import Path
import pytest
from importer import (
DuplicateOrderError,
InvalidRowError,
Order,
OrderImportError,
atomic_write,
import_orders,
)
HEADER = "order_id,customer,amount,status\n"
def write_csv(path: Path, *rows: str) -> Path:
path.write_text(HEADER + "".join(row + "\n" for row in rows), encoding="utf-8")
return path
def test_valid_rows_become_orders(tmp_path):
path = write_csv(tmp_path / "o.csv", "ORD-1,Бат,25000,paid", "ORD-2,Сараа,1500,pending")
result = import_orders(path)
assert result.orders == [
Order("ORD-1", "Бат", 25000, "paid"),
Order("ORD-2", "Сараа", 1500, "pending"),
]
assert result.errors == []
def test_bad_amount_keeps_cause(tmp_path):
path = write_csv(tmp_path / "o.csv", "ORD-1,Бат,45к,paid")
[error] = import_orders(path).errors
assert error.line == 2
assert isinstance(error.__cause__, ValueError)
def test_unknown_status_is_reported(tmp_path):
path = write_csv(tmp_path / "o.csv", "ORD-1,Бат,25000,unknown")
[error] = import_orders(path).errors
assert "unknown" in error.reason
def test_duplicate_is_an_invalid_row(tmp_path):
path = write_csv(tmp_path / "o.csv", "ORD-1,Бат,25000,paid", "ORD-1,Бат,25000,paid")
result = import_orders(path)
assert len(result.orders) == 1
[error] = result.errors
assert isinstance(error, DuplicateOrderError)
assert isinstance(error, InvalidRowError)
assert error.line == 3
def test_missing_file_chains_cause(tmp_path):
with pytest.raises(OrderImportError) as info:
import_orders(tmp_path / "байхгүй.csv")
assert isinstance(info.value.__cause__, FileNotFoundError)
def test_atomic_write_keeps_old_file_on_error(tmp_path):
target = tmp_path / "summary.txt"
target.write_text("хуучин", encoding="utf-8")
with pytest.raises(ValueError):
with atomic_write(target) as f:
f.write("шинэ")
raise ValueError("тасарлаа")
assert target.read_text(encoding="utf-8") == "хуучин"
assert not (tmp_path / "summary.txt.tmp").exists()write_csv туслах функц тест бүрт жижиг CSV файлыг tmp_path хавтаст үүсгэнэ: Хичээл 6.4-ийн fixture. [error] = ... хэлбэрийн unpacking хоёр ажлыг нэг мөрөнд хийнэ: жагсаалтаас ганц элементийг авах, мөн жагсаалт яг нэг элементтэй гэдгийг батлах. Хоёр алдаа гарсан бол энэ мөр өөрөө ValueError шидэж, тест бүтэлгүйтнэ.
Тестүүд Түвшин 4-ийн гурван сэдвийг шалгана. Order объектуудын тэнцүү байдлыг шалгахад frozen dataclass-ийн автомат __eq__ ажиллана. __cause__ шалгалтууд raise from гинж зөв байгааг баталгаажуулна. isinstance шалгалт шатлал зөв байгааг баталгаажуулна. Сүүлийн тест atomic_write-ийн гол амлалтыг шалгана.
python -m pytest -q...... [100%]
6 passed in 0.06sХурдны тест бичээгүйг анзаараарай, жишээлбэл “20,000 мөр нэг секундээс хурдан”. Ийм тест өөр компьютер дээр, эсвэл компьютер завгүй үед санамсаргүйгээр бүтэлгүйтдэг: тестийн үр дүн кодоос биш, орчноос хамаарна. Хурдыг measure.py шиг тусдаа хэмжилтийн script-ээр шалгана, тестүүд зөв ажиллагааг шалгана.
.gitignore файлд үүсгэгдсэн өгөгдөл ба cache-уудыг нэм:
orders.csv
orders_*.csv
summary.txt
__pycache__/
.mypy_cache/
.pytest_cache/git add generate_orders.py importer.py measure.py test_importer.py pyproject.toml .gitignore
git commit -m "Түвшин 4: захиалгын импортлогч — typing, exception шатлал, хэмжилт"Өгөгдлийн файлыг commit хийгээгүй: generate_orders.py түүнийг хэзээ ч, яг ижлээр нь дахин үүсгэнэ.
Хүсвэл импортлогчид дүнгийн хязгаарын шалгалт нэмж үзээрэй: тэг эсвэл сөрөг дүнтэй мөрийг InvalidRowError болгох, түүнд зориулсан тест бичих. Сонирхвол OrderImportError-ийн өөр нэг хүүхэд, жишээлбэл толгой мөрөнд шаардлагатай багана дутуу байхад импортыг шууд зогсоодог MissingColumnError нэмж болно: reader.fieldnames нь толгой мөрийн нэрсийг өгнө. Мөн хамгийн их зарцуулсан арван хэрэглэгчийг хураангуйд нэмэхдээ Хичээл 12.2-ын slow_report-ийн алдааг давтахгүй байх нь энэ түвшний бүх зүйлийг нэг дор шалгах сонирхолтой дасгал. Эдгээр бүгд санал бөгөөд дараагийн хичээлүүд үүнийг хийсэн гэж үзэхгүй.
error: Argument 4 to “Order” has incompatible type “str”; expected “Literal[‘paid’, ‘pending’, ‘cancelled’]” [arg-type]. “Order-ийн 4 дэх аргумент str төрөлтэй, гурван утгын аль нэгийг хүлээсэн” гэсэн утгатай. parse_row-д status-ийг if row["status"] not in VALID_STATUSES: raise ... гэж шалгаад шууд Order-д дамжуулахад mypy үүнийг гаргана. Хүн шалгалтын дараа утга зөв гэдгийг мэднэ, гэвч mypy шалгалтын утгыг төрөлд шилжүүлдэггүй. STATUS_BY_NAME dict-ээс хайх нь шалгалт ба төрлийг нэг дор шийддэг.
KeyError: ‘order_id’. “order_id гэдэг түлхүүр алга” гэсэн утгатай, импорт бүхэлдээ унана. Excel-ээс “CSV UTF-8” хэлбэрээр хадгалсан файлд түгээмэл: Excel файлын эхэнд BOM буюу “byte order mark”, үл харагдах тэмдэгт нэмдэг. csv модуль түүнийг эхний баганын нэрийн нэг хэсэг гэж уншиж, “order_id”-ийн оронд “\ufefforder_id” гэсэн нэр гарна. Шийдэл: файлыг encoding="utf-8-sig"-ээр нээх. Энэ кодлол BOM байвал хасаж, байхгүй бол энгийн UTF-8 шиг ажилладаг. Монгол хэрэглэгчид Excel-ээс экспорт хийх нь маш түгээмэл тул бодит импортлогчид энэ кодлолыг хэрэглэх нь зөв.
CSV файлд мөр бүрийн хооронд хоосон мөр гарах. Мессежгүй, Windows-ийн тусгай асуудал. csv.writer-ийг newline=""-гүйгээр нээсэн файлд хэрэглэхэд Windows мөрийн төгсгөл бүрийг хоёр дахин бичиж, Excel-д файлыг нээхэд мөр бүрийн хооронд хоосон мөр харагдана. csv модулийн баримт бичиг файлыг унших, бичих аль алинд нь newline=""-тэй нээхийг шаарддаг.
Хурдны тестүүд санамсаргүй бүтэлгүйтэх. Мессежгүй, Алхам 8-д тайлбарласан. Хэмжилтийг тестээс тусгаарла.
Profiler-т in харагдахгүй байхад халуун цэгийг өөр газар хайх. Мессежгүй. Алхам 4-ийн хүснэгтэд seen-ийн хайлт тусдаа мөр биш, import_orders-ийн tottime-д нуугдсан. Хүснэгтийн мөр бүрийг уншаад "аль ч функц удаан биш" гэж дүгнэвэл халуун цэгийг алдана. Функцийн tottime өндөр боловч дуудсан функцүүд нь хурдан бол удаан хэсэг тэр функцийн өөрийн кодонд: операторууд, давталтууд, хайлтууд.
Захиалгын импортлогч Түвшин 4-ийн гурван сэдвийг нэгтгэсэн. Төрлүүд: Literal-ээр зөвшөөрөгдсөн төлвүүд, frozen dataclass-аар өөрчлөгддөггүй захиалга, mypy-ийн strict горимоор шалгагдсан бүх функц, dict-ээс хайлтаар string-ийг Literal төрөлд аюулгүй хөрвүүлэх. Алдаа: OrderImportError үндэстэй шатлал, мөрийн алдааг цуглуулж импортыг үргэлжлүүлэх ба файлын алдаагаар бүхэлдээ зогсоох ялгаа, raise from-оор хадгалсан анхны шалтгаан, давталт доторх try/except/else, хураангуйг хагас бичигдэхээс хамгаалах atomic_write. Хурд: шууд хувилбар квадрат өсөлттэй байсныг хэмжилт харуулсан, cProfile функц хүртэлх нарийвчлалаар хаана харахыг заасан, list-ийг set-ээр сольсон хоёр мөрийн засвар 40,000 мөрт 48 дахин хурдасгасныг хэмжээг өөрчилсөн хэмжилт баталсан. Тестүүд зөв ажиллагааг шалгадаг, хурдыг тусдаа script хэмждэг.
Ингээд Түвшин 4 дууслаа. Энэ түвшинд таны код илүү найдвартай болсон: төрөл нь шалгагдсан, алдаа нь төлөвлөгдсөн, хурд нь хэмжигдсэн. Гэвч бүх програм маань нэг зүйлийг нэг дор л хийсэн: нэг мөр, нэг файл, нэг хүсэлт. Дараагийн түвшин, сургалтын сүүлчийнх, зэрэгцээ ажиллагаа буюу concurrency-д зориулагдсан: програм хэд хэдэн ажлыг нэг дор, жишээлбэл олон файл татах эсвэл олон хүсэлтийг хүлээх үед хэрхэн ажиллах вэ. Бүлэг 12-т сурсан хэмжилтийн загвар тэнд ч гол хэрэгсэл хэвээр байх болно: зэрэгцээ ажиллагааны тухай зөн совин хурдны тухай зөн совингоос ч илүү олон удаа буруу байдаг.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.