Skriptdan loyihaga: nega tuzilish kerak
Siz qayerdasiz
Python sintaksisini bilasiz. Funksiya yozasiz, sinf yozasiz, OOP tushunasiz. Endi savol boshqacha: yozgan kodingizga ishonsa bo'ladimi?
Ikki xil kod bor. Birinchisi — bir marta ishlatiladigan skript: ochdingiz, ishga tushirdingiz, natijani oldingiz, yopdingiz. Ikkinchisi — boshqa odam (yoki olti oydan keyingi siz) o'qiydigan, o'zgartiradigan, kengaytiradigan dastur.
Bu kitob ikkinchisi haqida. Mavzular: tuzilish, tur, test, xato, jurnal, konfiguratsiya, tezlik, paketlash. Ya'ni "ishladi" dan "ishonchli" gacha bo'lgan yo'l.
Kitob qanday tuzilgan
Har bobda kod haqiqatan ishga tushadi. Kitobdagi har bir "Chiqish" bloki — pytest, mypy, ruff
yoki Python ning o'zi qaytargan haqiqiy natija. Qaytish kodi ham ko'rsatiladi, shuning uchun xato
bo'lganda uni yashirib bo'lmaydi.
Kitob davomida bitta loyiha quramiz: hisobot — CSV faylni tekshirib, tozalab, hisobot
chiqaradigan buyruq qatori asbobi. 17-bobga borganda u to'liq: turlari belgilangan, testlari bor,
xatolarni to'g'ri qaytaradi, sozlanadi va o'rnatiladi.
Bitta fayldan boshlaymiz
Ko'pchilik shunday boshlaydi. Quyidagi skript CSV o'qiydi va o'rtacha bahoni chiqaradi:
shu faylni yaratingsinov_malumot.csv
ism,guruh,baho,sana Aziz,A,85,2026-09-01 Bobur,A,92,2026-09-01 Dilnoza,B,78,2026-09-02 Elyor,B,,2026-09-02 Feruza,A,95,2026-09-03 Gulnora,C,61,2026-09-03 Hasan,C,abc,2026-09-04 Iroda,B,88,2026-09-04
shu faylni yaratingskript.py
import csv jami = 0 soni = 0 with open("sinov_malumot.csv") as f: for qator in csv.DictReader(f): jami += int(qator["baho"]) soni += 1 print("o'rtacha:", jami / soni)
python3 skript.py
Traceback (most recent call last):
File "/mnt/c/Users/Admin/Desktop/Course Material/Python_Muhandisligi/ish/skript.py", line 7, in <module>
jami += int(qator["baho"])
ValueError: invalid literal for int() with base 10: ''
Qaytish kodi 1 — skript yiqildi. Sabab sinov_malumot.csv da: Elyorning bahosi bo'sh, Hasanniki
esa abc. int("") ishlamaydi.
Bu birinchi saboq: haqiqiy ma'lumot har doim iflos. Skript "toza ma'lumot" ni nazarda tutgan edi, ma'lumot esa bunga rozi emas.
Tuzatamiz... va muammo boshlanadi
shu faylni yaratingskript2.py
import csv jami = 0 soni = 0 xatolar = 0 with open("sinov_malumot.csv") as f: for qator in csv.DictReader(f): try: jami += int(qator["baho"]) soni += 1 except ValueError: xatolar += 1 print("o'rtacha:", round(jami / soni, 2), "| tashlab yuborilgan:", xatolar)
python3 skript2.py
o'rtacha: 83.17 | tashlab yuborilgan: 2
Ishladi. Lekin endi savollar boshlanadi:
- Qaysi qatorlar tashlab yuborildi? Skript aytmaydi.
baho500 bo'lsa-chi?int("500")ishlaydi, lekin baho 0–100 bo'lishi kerak edi.- Guruh bo'yicha o'rtacha kerak bo'lsa?
- Bu kodni qanday sinab ko'rasiz?
printga qarabmi?
Har bir savolga javob skript2.py ga yana bir nechta qator qo'shadi. Yigirmata savoldan keyin fayl
300 qator bo'ladi va uni hech kim o'zgartirishga jur'at eta olmaydi.
Muammoning ildizi: hamma narsa aralashgan
skript2.py da to'rtta butunlay boshqa ish bitta joyda turibdi:
| Ish | Kodda |
|---|---|
| O'qish (fayldan olish) | open, csv.DictReader |
| Tekshirish (to'g'rimi) | try/except ValueError |
| Hisoblash (o'rtacha) | jami / soni |
| Ko'rsatish (chiqarish) | print |
Ular aralashganda har biri boshqasini ushlab turadi:
- Hisoblashni sinab ko'rmoqchisiz — lekin buning uchun fayl kerak.
- Chiqishni o'zgartirmoqchisiz — hisoblashga tegishingiz kerak.
- Tekshiruvni qattiqroq qilmoqchisiz — o'qish sikliga kirasiz.
Ajratish shuning uchun kerak: har qism alohida sinaladi, alohida o'zgaradi.
Ajratilgan variant
shu faylni yaratinghisobot_v1.py
import csv from pathlib import Path def oqish(yol): """CSV dan lug'atlar ro'yxatini qaytaradi. Faqat o'qiydi, hech narsa tekshirmaydi.""" with open(yol, encoding="utf-8") as f: return list(csv.DictReader(f)) def tekshir(qatorlar): """(to'g'ri qatorlar, xatolar ro'yxati) qaytaradi. Fayl haqida hech narsa bilmaydi.""" yaxshi, xatolar = [], [] for i, q in enumerate(qatorlar, start=2): # 2 — sarlavhadan keyingi birinchi qator try: baho = int(q["baho"]) except ValueError: xatolar.append((i, q["ism"], f"baho son emas: {q['baho']!r}")) continue if not 0 <= baho <= 100: xatolar.append((i, q["ism"], f"baho oralig'idan tashqarida: {baho}")) continue yaxshi.append({**q, "baho": baho}) return yaxshi, xatolar def guruh_ortachasi(qatorlar): """Guruh -> o'rtacha baho. Hech qanday kiritish/chiqarish yo'q.""" yigindi = {} for q in qatorlar: g = q["guruh"] jami, soni = yigindi.get(g, (0, 0)) yigindi[g] = (jami + q["baho"], soni + 1) return {g: round(jami / soni, 2) for g, (jami, soni) in sorted(yigindi.items())} def chiqar(ortachalar, xatolar): for g, o in ortachalar.items(): print(f" {g}: {o}") if xatolar: print(f"xatolar ({len(xatolar)}):") for qator, ism, sabab in xatolar: print(f" {qator}-qator ({ism}): {sabab}") if __name__ == "__main__": qatorlar = oqish(Path("sinov_malumot.csv")) yaxshi, xatolar = tekshir(qatorlar) print(f"o'qildi: {len(qatorlar)}, to'g'ri: {len(yaxshi)}") chiqar(guruh_ortachasi(yaxshi), xatolar)
python3 hisobot_v1.py
o'qildi: 8, to'g'ri: 6 A: 90.67 B: 83.0 C: 61.0 xatolar (2): 5-qator (Elyor): baho son emas: '' 8-qator (Hasan): baho son emas: 'abc'
Kod uzunroq bo'ldi — skript.py 9 qator edi, hisobot_v1.py esa 50. Deyarli olti barobar.
Lekin chiqishga qarang: endi u qaysi qator, kim va nima sababdan rad etilganini aytadi (5-qator Elyor, 8-qator Hasan), va guruh bo'yicha natija beradi. Avvalgi skript esa faqat bitta son chiqarardi — va o'sha son ikkita talabani jim tashlab yuborgan bo'lardi.
Uzun kod yaxshi degani emas. Lekin bu uzunlik ish qilyapti.
Endi sinash mumkin
Asosiy foyda shu. guruh_ortachasi faylga ham, print ga ham bog'liq emas — demak uni to'g'ridan-to'g'ri
chaqirib tekshirsa bo'ladi:
import sys sys.path.insert(0, str(ISH)) from hisobot_v1 import guruh_ortachasi, tekshir namuna = [{"guruh": "A", "baho": 80}, {"guruh": "A", "baho": 90}, {"guruh": "B", "baho": 70}] print("guruh_ortachasi:", guruh_ortachasi(namuna)) xom = [{"ism": "X", "guruh": "A", "baho": "85"}, {"ism": "Y", "guruh": "A", "baho": "abc"}, {"ism": "Z", "guruh": "B", "baho": "500"}] yaxshi, xatolar = tekshir(xom) print("to'g'ri:", len(yaxshi), "| xatolar:", xatolar)
guruh_ortachasi: {'A': 85.0, 'B': 70.0}
to'g'ri: 1 | xatolar: [(3, 'Y', "baho son emas: 'abc'"), (4, 'Z', "baho oralig'idan tashqarida: 500")]
Fayl ochilmadi, print ga qaralmadi. Funksiyaga kirish berildi, chiqish tekshirildi. 6-bobda buni
pytest bilan avtomatlashtiramiz — hozircha muhimi shu: ajratilgan kodni sinash mumkin,
aralashganini yo'q.
Bir qadam orqaga: bu qancha turadi
Ajratishning narxi bor — ko'proq kod, ko'proq fayl, ko'proq nom o'ylab topish. Shuning uchun uni har doim qilish kerak emas.
| Vaziyat | Nima qilish |
|---|---|
| Bir marta ishlatiladigan hisob | Skript yozing, ajratmang |
| Har hafta ishlatiladigan | Funksiyalarga ajrating |
| Boshqa odam ishlatadi | + testlar, xato xabarlari |
| Boshqa kod import qiladi | + paket tuzilishi, turlar |
| Serverda ishlaydi | + jurnal, konfiguratsiya, monitoring |
Har qatorda oldingisi saqlanadi. Bu kitob shu jadval bo'ylab pastga tushadi.
:::tip Eng keng tarqalgan xato Boshlovchilar odatda kam ajratadi (hamma narsa bitta faylda). Tajribaliroq dasturchilar ko'pincha ortiqcha ajratadi: bitta funksiya uchun sinf, bitta sinf uchun interfeys, hech qachon almashtirilmaydigan narsa uchun "moslashuvchanlik". Ikkalasi ham xarajat. Qoida oddiy: ajratish bugun bir narsani osonlashtirsa — ajrating. "Kelajakda kerak bo'lishi mumkin" — bu sabab emas. :::
if __name__ == "__main__" nima uchun kerak
hisobot_v1.py oxiridagi shart tasodifiy emas. Usiz faylni import qilish ham uni ishga tushirgan
bo'lardi:
shu faylni yaratingshartsiz.py
print("bu modul yuklanganda ishlaydi") def qosh(a, b): return a + b natija = qosh(2, 3) print("hisoblandi:", natija)
shu faylni yaratingshartli.py
def qosh(a, b): return a + b if __name__ == "__main__": print("bu faqat to'g'ridan-to'g'ri ishga tushirilganda ishlaydi") print("hisoblandi:", qosh(2, 3))
python3 -c "import shartsiz; print('--- import tugadi ---')" python3 -c "import shartli; print('import tugadi, hech narsa chop etilmadi')"
bu modul yuklanganda ishlaydi hisoblandi: 5 --- import tugadi --- import tugadi, hech narsa chop etilmadi
Birinchi holatda oddiy import ikki qator chiqardi — biz undan faqat qosh funksiyasini olmoqchi
edik. Ikkinchisida import jim o'tdi.
Bu muhim, chunki test ham import qiladi. Moduli import qilinganda hisob-kitob boshlab yuboradigan fayl testda ham, boshqa modulda ham muammo tug'diradi.
__name__ nima? Python har modulga shu o'zgaruvchini beradi:
import sys sys.path.insert(0, str(ISH)) import shartli print("import qilinganda __name__ =", repr(shartli.__name__)) print("to'g'ridan-to'g'ri ishga tushirilganda __name__ =", repr("__main__"))
import qilinganda __name__ = 'shartli' to'g'ridan-to'g'ri ishga tushirilganda __name__ = '__main__'
Loyiha tuzilishi qanday bo'ladi
Keyingi boblarda hisobot ni to'liq paketga aylantiramiz. Oxirgi ko'rinishi taxminan shunday:
hisobot/
├── pyproject.toml # paket haqida ma'lumot va bog'liqliklar (2-bob)
├── README.md
├── src/
│ └── hisobot/
│ ├── __init__.py
│ ├── modellar.py # dataclass lar (4-bob)
│ ├── oqish.py # fayldan o'qish (10-bob)
│ ├── tekshiruv.py # qoidalar (5-, 8-boblar)
│ ├── hisob.py # statistika
│ ├── sozlama.py # konfiguratsiya (11-bob)
│ └── cli.py # buyruq qatori (15-bob)
└── tests/
├── test_tekshiruv.py # (6-, 7-boblar)
└── test_hisob.py
Diqqat qiling: src/ ichida paket, tests/ yonida. Bu tasodifiy emas va 3-bobda nega aynan shunday
ekanini o'lchab ko'rsatamiz.
Xulosa
| Fikr | Mazmuni |
|---|---|
| Iflos ma'lumot | Haqiqiy fayl har doim kutilmagan qiymat beradi — skript birinchi shunda yiqiladi |
| To'rt ish | O'qish, tekshirish, hisoblash, ko'rsatish — aralashtirmang |
| Sinovchanlik | Ajratilgan funksiyani chaqirib tekshirish mumkin; aralashganini yo'q |
| Xato xabari | "Ishlamadi" emas, "5-qator, Hasan, baho son emas" |
__main__ sharti |
Modul import qilinganda hech narsa bajarilmasin |
| Ajratish narxi | Har doim emas: bugungi ishni osonlashtirsa — ha |
Talaba ishi
sinov_malumot.csvga yangi buzuq qator qo'shing: guruhi bo'sh bo'lgan talaba.hisobot_v1.pynima qiladi? Bu to'g'ri xulqmi?tekshirfunksiyasiga yangi qoida qo'shing:ismbo'sh bo'lmasin. Faqat bitta funksiyaga tegishingiz kerak bo'ldimi?guruh_ortachasiga bo'sh ro'yxat bering. Nima bo'ladi? Bu yerda qanday xulq to'g'ri bo'lardi — xato, bo'sh lug'at, yokiNone?hisobot_v1.pydagichiqarfunksiyasini o'zgartirib, natijani JSON qilib chiqaring.guruh_ortachasiga tegishingiz kerak bo'ldimi?
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.