Asosiy qismga o'tish

Yakuniy loyiha: talabalar jurnali

22-bob 10 kod bloki 2679 so'z

Bu bobda

  • kitobdagi hamma mavzuni bitta ishlaydigan dasturda birlashtirishni
  • dasturni modul, klass va funksiyalarga bo'lishni
  • ma'lumotni JSON da saqlab, hisobotni txt ga chiqarishni
  • foydalanuvchi bilan menyu orqali muloqot qilishni
  • dasturni bosqichma-bosqich sinab ko'rishni

Vazifa

O'qituvchi uchun kichik dastur kerak:

  1. Talaba qo'shish (ism, guruh).
  2. Talabaga fan bo'yicha baho qo'yish (2–5).
  3. Har bir talabaning o'rtacha bahosi va guruh reytingi.
  4. Ma'lumot dastur yopilganda yo'qolmasin — JSON faylda saqlansin.
  5. Reytingni chiroyli txt hisobot qilib chiqarish.
  6. Noto'g'ri kiritish (harf, 7 baho, yo'q talaba) dasturni yiqitmasin.

Qaysi bobdan nima kerak bo'ladi:

Qism Nima ishlatamiz Bob
Talaba ma'lumoti @dataclass, ro'yxat, lug'at 9, 11, 18
Jurnal klass, metodlar, @property 16, 18
Saqlash JSON, asdict, ** 14, 21
Hisobot txt fayl, f-string tekislash 5, 19
Tekshiruv raise, try/except 20
Menyu while True, if/elif, funksiyalar 6, 8, 13
Tuzilma alohida modul 15

1-qadam: model — jurnal.py

Butun "biznes mantiq" bitta modulda. Menyu (foydalanuvchi bilan gaplashish) undan alohida bo'ladi: shunda bir xil modulni keyin Telegram bot yoki veb-sayt bilan ham ishlatish mumkin.

Fayl: jurnal.py

python73 qator
"""Talabalar jurnali: talabalar, baholar, JSON ga saqlash va txt hisobot."""
import json
from dataclasses import asdict, dataclass, field
from pathlib import Path

BAHOLAR = (2, 3, 4, 5)


@dataclass
class Talaba:
    ism: str
    guruh: str
    baholar: dict = field(default_factory=dict)

    def baho_qosh(self, fan, baho):
        if baho not in BAHOLAR:
            raise ValueError(f"Baho 2 dan 5 gacha bo'lishi kerak, berildi: {baho}")
        self.baholar.setdefault(fan, []).append(baho)

    @property
    def ortacha(self):
        hammasi = [b for royxat in self.baholar.values() for b in royxat]
        return sum(hammasi) / len(hammasi) if hammasi else 0.0


class Jurnal:
    def __init__(self, fayl="jurnal.json"):
        self.fayl = Path(fayl)
        self.talabalar = {}
        self.yukla()

    def yukla(self):
        try:
            royxat = json.loads(self.fayl.read_text(encoding="utf-8"))
        except FileNotFoundError:
            return
        except json.JSONDecodeError:
            print(f"Ogohlantirish: {self.fayl} buzilgan, bo'sh jurnal ochildi")
            return
        self.talabalar = {d["ism"]: Talaba(**d) for d in royxat}

    def saqla(self):
        royxat = [asdict(t) for t in self.talabalar.values()]
        self.fayl.write_text(json.dumps(royxat, ensure_ascii=False, indent=2), encoding="utf-8")

    def talaba_qosh(self, ism, guruh):
        ism = ism.strip().title()
        if not ism:
            raise ValueError("Ism bo'sh bo'lmasin")
        if ism in self.talabalar:
            raise ValueError(f"{ism} allaqachon bor")
        self.talabalar[ism] = Talaba(ism, guruh.strip().upper())
        return self.talabalar[ism]

    def topish(self, ism):
        talaba = self.talabalar.get(ism.strip().title())
        if talaba is None:
            raise KeyError(f"{ism} degan talaba yo'q")
        return talaba

    def reyting(self, guruh=None):
        royxat = [t for t in self.talabalar.values() if guruh is None or t.guruh == guruh]
        return sorted(royxat, key=lambda t: t.ortacha, reverse=True)

    def hisobot(self, fayl="hisobot.txt"):
        sarlavha = "#".ljust(3) + "Talaba".ljust(20) + "Guruh".ljust(7) + "Baho".rjust(6) + "O'rtacha".rjust(10)
        qatorlar = [sarlavha, "-" * len(sarlavha)]
        for i, t in enumerate(self.reyting(), start=1):
            soni = sum(len(r) for r in t.baholar.values())
            qatorlar.append(f"{i:<3}{t.ism:<20}{t.guruh:<7}{soni:>6}{t.ortacha:>10.2f}")
        matn = "\n".join(qatorlar) + "\n"
        Path(fayl).write_text(matn, encoding="utf-8")
        return matn

Bu yerda ikkita yangi narsa bor:

  • self.baholar.setdefault(fan, []) — lug'atda fan bo'lmasa, uni bo'sh ro'yxat bilan yaratadi va ro'yxatni qaytaradi. .append(baho) shu ro'yxatga qo'shadi. Bu 11-bobdagi get(k, 0) + 1 andozasining ro'yxatlar uchun varianti.
  • [b for royxat in ... for b in royxat] — ikki for li comprehension: "har bir fan ro'yxatidagi har bir baho". Ichma-ich siklni bir qatorda yozish.
  • "Talaba".ljust(20) — satrni 20 belgigacha o'ngdan probel bilan to'ldiradi, rjust — chapdan. Bu f"{x:<20}" ning metod shakli; sarlavhada O'rtacha so'zidagi apostrof f-string ichida muammo tug'dirmasligi uchun shunday yozdik.

2-qadam: modulni sinab ko'rish

Menyu yozishdan oldin, mantiq to'g'ri ishlashini kod bilan tekshiramiz. Toza boshlash uchun eski faylni o'chiramiz:

python18 qator
from pathlib import Path
from jurnal import Jurnal

Path("jurnal.json").unlink(missing_ok=True)

j = Jurnal()
j.talaba_qosh("sarvinoz karimova", "ai-1")
j.talaba_qosh("ali valiyev", "ai-1")
j.talaba_qosh("otabek", "ai-2")

j.topish("Sarvinoz Karimova").baho_qosh("Python", 5)
j.topish("sarvinoz karimova").baho_qosh("Matematika", 4)
j.topish("Ali Valiyev").baho_qosh("Python", 4)
j.topish("Ali Valiyev").baho_qosh("Python", 3)
j.topish("Otabek").baho_qosh("Python", 5)

for t in j.reyting():
    print(f"{t.ism:<18}{t.guruh:<6}{t.ortacha:.2f}  {t.baholar}")
Natija
Otabek            AI-2  5.00  {'Python': [5]}
Sarvinoz Karimova AI-1  4.50  {'Python': [5], 'Matematika': [4]}
Ali Valiyev       AI-1  3.50  {'Python': [4, 3]}

topish() ismni title() qilib qidiradi, shuning uchun katta-kichik harf muhim emas.

Xatolarni ham sinaymiz — ular dasturni yiqitmasligi, balki tushunarli xabar berishi kerak:

python11 qator
sinovlar = [
    lambda: j.talaba_qosh("Otabek", "ai-2"),
    lambda: j.talaba_qosh("   ", "ai-1"),
    lambda: j.topish("Javohir"),
    lambda: j.topish("Otabek").baho_qosh("Python", 7),
]
for sinov in sinovlar:
    try:
        sinov()
    except (ValueError, KeyError) as e:
        print(f"{type(e).__name__}: {e}")
Natija
ValueError: Otabek allaqachon bor
ValueError: Ism bo'sh bo'lmasin
KeyError: "Javohir degan talaba yo'q"
ValueError: Baho 2 dan 5 gacha bo'lishi kerak, berildi: 7

Har bir sinovni lambda ichiga o'radik, shunda ular ro'yxat yaratilganda emas, sikl ichida, try ostida chaqiriladi.

3-qadam: saqlash va qayta yuklash

python6 qator
j.saqla()
print(Path("jurnal.json").read_text(encoding="utf-8")[:260], "...")

yangi = Jurnal()
print("Qayta yuklandi:", len(yangi.talabalar), "talaba")
print("Sarvinozning o'rtachasi:", yangi.topish("sarvinoz karimova").ortacha)
Natija
[
  {
    "ism": "Sarvinoz Karimova",
    "guruh": "AI-1",
    "baholar": {
      "Python": [
        5
      ],
      "Matematika": [
        4
      ]
    }
  },
  {
    "ism": "Ali Valiyev",
    "guruh": "AI-1",
    "baholar": {
      "Python": [
        4, ...
Qayta yuklandi: 3 talaba
Sarvinozning o'rtachasi: 4.5

Jurnal() yangidan yaratildi va ma'lumotni fayldan o'qidi — dastur "eslab qoldi".

Hisobot:

python1 qator
print(yangi.hisobot())
Natija
#  Talaba              Guruh    Baho  O'rtacha
----------------------------------------------
1  Otabek              AI-2        1      5.00
2  Sarvinoz Karimova   AI-1        2      4.50
3  Ali Valiyev         AI-1        2      3.50

hisobot.txt fayli ham yaratildi — uni o'qituvchi chop etishi yoki Telegramga yuborishi mumkin.

4-qadam: menyu

Endi foydalanuvchi bilan gaplashadigan qism. U jurnal modulidan foydalanadi va har bir amalni alohida funksiyada bajaradi:

Fayl: dastur.py

python64 qator
from jurnal import Jurnal

MENYU = """
1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish"""


def talaba_qosh(j):
    j.talaba_qosh(input("Ism familiya: "), input("Guruh: "))
    print("Qo'shildi.")


def baho_qoy(j):
    talaba = j.topish(input("Talaba: "))
    fan = input("Fan: ").strip().capitalize()
    matn = input("Baho (2-5): ").strip()
    if not matn.isdigit():
        raise ValueError(f"'{matn}' — son emas")
    baho = int(matn)
    talaba.baho_qosh(fan, baho)
    print(f"{talaba.ism}: {fan} — {baho}")


def reyting(j):
    for i, t in enumerate(j.reyting(), start=1):
        print(f"{i}. {t.ism} ({t.guruh}) — {t.ortacha:.2f}")


def hisobot(j):
    j.hisobot()
    print("hisobot.txt yozildi.")


AMALLAR = {"1": talaba_qosh, "2": baho_qoy, "3": reyting, "4": hisobot}


def main():
    j = Jurnal()
    print(f"Jurnal ochildi: {len(j.talabalar)} talaba.")
    while True:
        print(MENYU)
        tanlov = input("Tanlang: ").strip()
        if tanlov == "0":
            j.saqla()
            print("Saqlandi. Xayr!")
            break
        amal = AMALLAR.get(tanlov)
        if amal is None:
            print("Bunday band yo'q.")
            continue
        try:
            amal(j)
            j.saqla()
        except ValueError as e:
            print("Xato:", e)
        except KeyError as e:
            print("Xato:", e.args[0])


if __name__ == "__main__":
    main()

E'tibor bering:

  • AMALLAR lug'ati menyu raqamini funksiyaga bog'laydi. Uzun if/elif zanjiri o'rniga — bitta get(). Yangi band qo'shish uchun bitta funksiya va lug'atga bitta qator yetadi.
  • Xatolar bitta joyda — main() dagi try da ushlanadi. baho_qoy() baho son emasligini o'zi tekshirib, tushunarli o'zbekcha ValueError beradi; Pythonning inglizcha invalid literal for int() xabari foydalanuvchiga ko'rinmaydi.
  • Har bir muvaffaqiyatli amaldan keyin j.saqla() — dastur kutilmaganda yopilsa ham ma'lumot yo'qolmaydi.
  • e.args[0] — KeyError xabarini qo'shtirnoqsiz olish uchun.

Sinab ko'ramiz. Kitobda kiritishlar oldindan berilgan; o'zingizda terminalda python dastur.py yoki Jupyterda %run dastur.py:

python2 qator
import dastur
dastur.main()
Natija
Jurnal ochildi: 3 talaba.

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 2
Talaba: ali valiyev
Fan: matematika
Baho (2-5): 5
Ali Valiyev: Matematika — 5

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 2
Talaba: javohir
Xato: javohir degan talaba yo'q

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 1
Ism familiya: javohir tursunov
Guruh: ai-2
Qo'shildi.

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 2
Talaba: javohir tursunov
Fan: python
Baho (2-5): besh
Xato: 'besh' — son emas

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 2
Talaba: Javohir Tursunov
Fan: Python
Baho (2-5): 4
Javohir Tursunov: Python — 4

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 9
Bunday band yo'q.

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 3
1. Otabek (AI-2) — 5.00
2. Sarvinoz Karimova (AI-1) — 4.50
3. Ali Valiyev (AI-1) — 4.00
4. Javohir Tursunov (AI-2) — 4.00

1 - talaba qo'shish
2 - baho qo'yish
3 - reyting
4 - hisobot (txt)
0 - chiqish
Tanlang: 0
Saqlandi. Xayr!

Jurnal fayldan 3 talaba bilan ochildi; noma'lum talaba, "besh" degan baho va mavjud bo'lmagan band dasturni yiqitmadi; chiqishda hammasi saqlandi.

Oxirgi tekshiruv — dastur qayta ochilganda Javohir va yangi baholar joyidami:

python5 qator
from jurnal import Jurnal

j = Jurnal()
print(len(j.talabalar), "talaba")
print(j.hisobot())
Natija
4 talaba
#  Talaba              Guruh    Baho  O'rtacha
----------------------------------------------
1  Otabek              AI-2        1      5.00
2  Sarvinoz Karimova   AI-1        2      4.50
3  Ali Valiyev         AI-1        3      4.00
4  Javohir Tursunov    AI-2        1      4.00

Loyihani qanday rivojlantirish mumkin

Bu dastur keyingi kurslar uchun asos. Masalan: - Telegram bot: dastur.py o'rniga bot yozing — jurnal.py o'zgarmaydi. - Grafik: matplotlib bilan guruhlar o'rtachasini ustunli diagrammada chizing. - Ma'lumotlar bazasi: JSON o'rniga SQLite (sqlite3 — standart kutubxonada bor). - Testlar: pytest bilan Talaba va Jurnal metodlarini avtomatik tekshirish — keyingi kitob "Python muhandisligi" shundan boshlanadi.

Xulosa: siz nimalarni o'rgandingiz

Bo'lim Mavzular
Asoslar (1–5) o'zgaruvchilar, turlar, arifmetika, math, satrlar
Boshqaruv (6–8) if, for, while, break, continue
Tuzilmalar (9–12) list, tuple, dict, set, comprehension
Funksiyalar (13–15) def, return, *args, lambda, modullar
OOP (16–18) klass, meros, kompozitsiya, maxsus metodlar, @property, dataclass
Fayllar (19–21) txt, try/except, JSON
Loyiha (22) hammasi birga

Endi siz kichik, lekin haqiqiy dastur yoza olasiz: u ma'lumotni saqlaydi, xatoga chidamli va qismlarga bo'lingan. Sun'iy intellekt muhandisligi yo'lidagi keyingi qadam — numpy, pandas va ma'lumotlar bilan ishlash.

Mashqlar

22.1

Bitta qatorda fan:baho juftlari berilgan, masalan python:5 matematika:4 python:3. Har bir fan bo'yicha o'rtacha bahoni, fanlar alifbo tartibida, fan o'rtacha ko'rinishida (2 xona bilan) chiqaring. Maslahat: setdefault(fan, []).append(baho).

KirishKutilgan natija
python:5 matematika:4 python:3matematika 4.00 python 4.00
ingliz:5 ingliz:5 ingliz:2ingliz 4.00
Yechimni ko'rish
python6 qator
fanlar = {}
for juft in input().split():
    fan, baho = juft.split(":")
    fanlar.setdefault(fan, []).append(int(baho))
for fan in sorted(fanlar):
    print(fan, f"{sum(fanlar[fan]) / len(fanlar[fan]):.2f}")
22.2

Menyu buyruqlari ketma-ket berilgan (har biri alohida qatorda): qosh <son>, ayir <son>, kor, chiq. Boshlang'ich qiymat 0. kor da joriy qiymatni chiqaring, chiq da to'xtang. Noto'g'ri buyruq yoki son bo'lmasa — Xato chiqaring va davom eting. Buyruqlarni AMALLAR lug'ati bilan bajaring.

KirishKutilgan natija
qosh 5, ayir 2, kor, qosh x, uchir 1, qosh 10, kor, chiq3 Xato Xato 13
kor, chiq0
Yechimni ko'rish
python25 qator
qiymat = 0


def qosh(n):
    global qiymat
    qiymat += n


def ayir(n):
    global qiymat
    qiymat -= n


AMALLAR = {"qosh": qosh, "ayir": ayir}
while True:
    buyruq = input().split()
    if buyruq == ["chiq"]:
        break
    if buyruq == ["kor"]:
        print(qiymat)
        continue
    try:
        AMALLAR[buyruq[0]](int(buyruq[1]))
    except (KeyError, IndexError, ValueError):
        print("Xato")

Mustaqil ish (yakuniy topshiriq)

jurnal.py ni kengaytiring va har bir qadamni sinab ko'ring:

  1. Jurnal.talaba_ochir(ism) — talabani o'chirish (yo'q bo'lsa KeyError).
  2. Talaba.fan_ortacha(fan) — bitta fan bo'yicha o'rtacha.
  3. Jurnal.eng_yaxshi(fan) — shu fan bo'yicha eng yuqori o'rtachali talaba.
  4. Menyuga 5- va 6-bandlarni qo'shing.
  5. Hisobotga har bir guruh uchun alohida reyting bo'limi qo'shing (reyting(guruh=...) tayyor).
  6. Bonus: ikkita jurnal faylini birlashtiradigan funksiya yozing (bir xil talaba ikkala faylda bo'lsa, baholarini qo'shing).

Bu bobni bepul o'qidingiz.

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

Telegramda yozish Narxlar