Asosiy qismga o'tish

Skriptdan loyihaga: nega tuzilish kerak

1-bob 13 kod bloki 1801 so'z

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

matn9 qator
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

python9 qator
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)
matn1 qator
python3 skript.py
Chiqish (qaytish kodi 1, 61 ms)
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

python13 qator
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)
matn1 qator
python3 skript2.py
Chiqish (qaytish kodi 0, 19 ms)
o'rtacha: 83.17 | tashlab yuborilgan: 2

Ishladi. Lekin endi savollar boshlanadi:

  • Qaysi qatorlar tashlab yuborildi? Skript aytmaydi.
  • baho 500 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? print ga 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

python50 qator
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)
matn1 qator
python3 hisobot_v1.py
Chiqish (qaytish kodi 0, 23 ms)
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:

python12 qator
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)
Chiqish
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

python7 qator
print("bu modul yuklanganda ishlaydi")

def qosh(a, b):
    return a + b

natija = qosh(2, 3)
print("hisoblandi:", natija)

shu faylni yaratingshartli.py

python6 qator
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))
python2 qator
python3 -c "import shartsiz; print('--- import tugadi ---')"
python3 -c "import shartli; print('import tugadi, hech narsa chop etilmadi')"
Chiqish (qaytish kodi 0, 37 ms)
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:

python5 qator
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__"))
Chiqish
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:

python15 qator
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

  1. sinov_malumot.csv ga yangi buzuq qator qo'shing: guruhi bo'sh bo'lgan talaba. hisobot_v1.py nima qiladi? Bu to'g'ri xulqmi?
  2. tekshir funksiyasiga yangi qoida qo'shing: ism bo'sh bo'lmasin. Faqat bitta funksiyaga tegishingiz kerak bo'ldimi?
  3. guruh_ortachasi ga bo'sh ro'yxat bering. Nima bo'ladi? Bu yerda qanday xulq to'g'ri bo'lardi — xato, bo'sh lug'at, yoki None?
  4. hisobot_v1.py dagi chiqar funksiyasini o'zgartirib, natijani JSON qilib chiqaring. guruh_ortachasi ga tegishingiz kerak bo'ldimi?

Bu bobni bepul o'qidingiz.

Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.

Telegramda yozish Narxlar