IlmHamroh
Python kursi/Standart kutubxona6/16-dars23 daqiqa
Mundarija (21)

15.6-dars: collections: Counter, defaultdict

15-QISM — STANDART KUTUBXONA · 6-dars


1. Kirish va motivatsiya

Dasturlashdagi eng ko'p uchraydigan ikki vazifa: sanash va guruhlash. "Qaysi mahsulot necha marta sotildi?", "har bir mijoz qaysi toifalardan xarid qildi?", "matnda qaysi so'z eng ko'p uchraydi?"

6-qismda bularni oddiy dict bilan yozdik:

python
sanoq = {}
for soz in sozlar:
    if soz not in sanoq:
        sanoq[soz] = 0
    sanoq[soz] += 1

Bu kod ishlaydi, lekin har safar bir xil andoza takrorlanadi va xato uchun joy qoladi. collections modulida buning uchun maxsus turlar bor:

python
Counter(sozlar).most_common(10)           # sanash va eng ko'plari
guruhlar = defaultdict(list)              # guruhlash
guruhlar[toifa].append(mahsulot)

Real vaziyat. Analitika xizmatida sahifalar ko'rishini defaultdict(int) bilan sanashdi. Hisobot sahifasi "bu sahifa ko'rilganmi?" degan tekshiruvni sanoq[sahifa] > 0 bilan bajarardi. Bir necha kundan keyin xotira bir necha gigabaytga o'sdi: har bir tekshiruv — hatto mavjud bo'lmagan, botlar so'ragan manzillar uchun ham — lug'atga yangi kalit qo'shayotgan edi. defaultdict ning bitta xususiyatini bilmaslik qimmatga tushdi.

Bu darsda Counter va defaultdict ni chuqur o'rganamiz: ular aynan nima qiladi, qayerda dict dan farq qiladi va qanday jim tuzoqlari bor.

Bu darsda:

  • Counter: yaratish, most_common, total, update, elements
  • Counter arifmetikasi: +, -, &, |, subtract, solishtirish
  • Counter va dict.update farqi, manfiy qiymatlar
  • defaultdict: list, set, int bilan guruhlash
  • O'qish kalit qo'shadi — in, get va xotira
  • __missing__ — mexanizmning o'zi
  • Ichma-ich lug'at, pickle va json
  • Amaliy: onlayn do'kon buyurtmalari tahlili

2. Nazariya — chuqur tushuntirish

2.1. Counter — sanovchi lug'at

Counter — dict ning vorisi: kalit — element, qiymat — soni.

python
from collections import Counter

c = Counter("abrakadabra")        # Counter({'a': 5, 'b': 2, 'r': 2, 'k': 1, 'd': 1})
Counter(["olma", "nok", "olma"])  # iterable dan
Counter({"olma": 3})              # lug'atdan
Counter(olma=3, nok=1)            # kalit so'zlardan
Amal Natija
c["z"] (yo'q kalit) 0 — xato emas, kalit qo'shilmaydi
del c["z"] (yo'q kalit) Xato bermaydi
c.most_common(n) Eng ko'p n ta; teng sonlarda — qo'shilish tartibida
c.most_common()[-1] Eng kami
c.total() Barcha sonlar yig'indisi (3.10+)
c.elements() Har elementni soni marta; ≤ 0 lar tashlanadi

2.2. update — qo'shadi, almashtirmaydi

dict.update Counter.update
{"x": 1} ga {"x": 5} {"x": 5} — almashtiradi {"x": 6} — qo'shadi
Iterable bilan Juftliklar kutiladi Har elementni sanaydi

Counter.subtract — ayiradi va manfiy qiymatlarni saqlaydi.

2.3. Multitoʻplam arifmetikasi

Counter ni multitoʻplam (bir element bir necha marta bo'la oladigan to'plam) sifatida ishlatish mumkin:

Amal Ma'nosi Manfiy va nol
a + b Sonlarni qo'shish Natijadan tashlanadi
a - b Ayirish Natijadan tashlanadi
a & b Minimum (kesishma) Tashlanadi
`a b` Maksimum (birlashma)
+a / -a Musbatlari / manfiylari (ishorasi almashtirilgan)
a.subtract(b) Ayirish joyida Saqlanadi
python
ombor = Counter(olma=3, nok=1)
buyurtma = Counter(olma=1, nok=2, anor=1)
buyurtma - ombor          # Counter({'nok': 1, 'anor': 1}) — yetishmayotgani
ombor - buyurtma          # Counter({'olma': 2}) — ortib qolgani
buyurtma <= ombor         # False — bajarib bo'lmaydi  (3.10+)

Solishtirish (3.10+): == yo'q kalitlarni 0 deb hisoblaydi: Counter(a=1) == Counter(a=1, b=0) → True. <=, >= — har element bo'yicha.

2.4. defaultdict — standart qiymatli lug'at

python
from collections import defaultdict

guruhlar = defaultdict(list)
guruhlar["meva"].append("olma")        # kalit yo'q → list() yaratiladi

sanoq = defaultdict(int)               # int() == 0
toifalar = defaultdict(set)            # takrorlarsiz guruh

defaultdict(fabrika) — fabrika argumentsiz chaqiriladigan har qanday narsa: list, set, int, dict, lambda: "noma'lum", o'z klassingiz.

Tanlov Qachon
Counter Faqat sanash kerak — qulayroq va boy API
defaultdict(int) Sanash + oddiy lug'at xulqi
defaultdict(list) Guruhlash
defaultdict(set) Takrorlarsiz guruhlash
dict.setdefault(k, []) Bir martalik, oddiy dict kerak bo'lsa

2.5. O'qish ham yozadi

defaultdict ning eng muhim xususiyati: yo'q kalitni d[k] bilan o'qish uni lug'atga qo'shadi.

Amal defaultdict Counter dict
d[yoq] Qo'shadi, standart qiymat 0, qo'shmaydi KeyError
d.get(yoq) None, qo'shmaydi None, qo'shmaydi None
yoq in d Tekshiradi, qo'shmaydi Tekshiradi Tekshiradi
python
sanoq = defaultdict(int)
if sanoq["/admin.php"] > 0: ...       # ❌ endi "/admin.php" lug'atda bor
if sanoq.get("/admin.php", 0) > 0: ...   # ✅

Oqibatlari: xotira o'sishi, len() noto'g'ri, JSON ga bo'sh yozuvlar tushishi, for tsikli ichida o'qish — RuntimeError: dictionary changed size during iteration.

Tashqariga (API, shablon, boshqa modul) defaultdict emas, dict(d) bering.

2.6. __missing__ — mexanizm

dict.__getitem__ kalitni topmasa, sinfda __missing__ bo'lsa uni chaqiradi. defaultdict va Counter aynan shu orqali ishlaydi:

Sinf __missing__ nima qiladi
defaultdict fabrika() ni chaqiradi, saqlaydi, qaytaradi
Counter 0 qaytaradi, saqlamaydi
O'z sinfingiz Istalgan xulq
python
class Tarjima(dict):
    def __missing__(self, kalit):
        return f"[{kalit}]"              # tarjima yo'q — kalitni ko'rsatish

__missing__ faqat d[k] da ishlaydi — d.get(k) va k in d da emas.

default_factory atributi o'zgartirilishi mumkin; None qilinsa — oddiy KeyError.

2.7. Ichma-ich lug'at, pickle, json

python
daraxt = lambda: defaultdict(daraxt)
d = daraxt()
d["uz"]["toshkent"]["chilonzor"] = 5     # oraliq darajalar avtomatik
Holat Natija
json.dumps(Counter(...)) / json.dumps(defaultdict(...)) Oddiy JSON obyekt
json.loads(...) Oddiy dict — turi qaytmaydi
pickle + defaultdict(int)
pickle + defaultdict(lambda: 0) PicklingError — lambda pickle bo'lmaydi
multiprocessing orqali uzatish Xuddi shu cheklov (14.4-dars)

pickle kerak bo'lsa — fabrika sifatida modul darajasidagi funksiya yoki klass.


3. Tez ma'lumotnoma

python
from collections import Counter, defaultdict

sanoq = Counter(sozlar)
sanoq.most_common(10); sanoq.total(); sanoq["yoq"]      # 0
sanoq.update(yangi_sozlar)                               # qo'shadi
yetishmaydi = buyurtma - ombor; bajarsa_boladi = buyurtma <= ombor

guruh = defaultdict(list)
for x in malumot:
    guruh[kalit(x)].append(x)
natija = dict(guruh)                                     # tashqariga oddiy dict
guruh.get(k, [])                                         # o'qish — qo'shmasdan

Qoidalar

sanash — Counter, guruhlash — defaultdict(list/set)
Counter.update qo'shadi, dict.update almashtiradi
Counter + - & | manfiy va nolni tashlaydi; subtract saqlaydi
defaultdict: d[k] o'qish kalit qo'shadi — tekshiruvda get / in
tashqariga dict(d)
pickle kerak — lambda emas, nomli funksiya

4. Batafsil misollar

Misol 1 — Counter asoslari

python
"""Counter yaratish usullari; yo'q kalit; most_common va teng sonlar; total; update va dict.update farqi; elements; so'z chastotasi."""

import re
from collections import Counter

MATN = """Python — o'rganish oson til. Python bilan veb, ma'lumotlar tahlili va
avtomatlashtirish qilinadi. O'rganish uchun eng yaxshi yo'l — kod yozish, kod o'qish
va yana kod yozish."""


def main() -> None:
    print("=== 1. Yaratish ===")
    print(f"  satrdan:       {Counter('abrakadabra')}")
    print(f"  ro'yxatdan:    {Counter(['olma', 'nok', 'olma'])}")
    print(f"  lug'atdan:     {Counter({'olma': 3, 'nok': 1})}")
    print(f"  kalit so'zdan: {Counter(olma=3, nok=1)}")

    print("\n=== 2. Yo'q kalit ===")
    c = Counter("abrakadabra")
    print(f"  c['z'] = {c['z']}, 'z' in c: {'z' in c}, len(c) = {len(c)}")
    del c["z"]
    print(f"  del c['z'] xato bermadi, len(c) = {len(c)}")

    print("\n=== 3. most_common va total ===")
    print(f"  most_common(2): {c.most_common(2)}")
    print(f"  eng kami:       {c.most_common()[-1]}")
    print(f"  total():        {c.total()} (satr uzunligi {len('abrakadabra')})")
    print(f"  teng sonlar — qo'shilish tartibida: {Counter('aab').most_common()} va {Counter('bba').most_common()}")

    print("\n=== 4. ⚠️ update: Counter qo'shadi, dict almashtiradi ===")
    oddiy = {"olma": 3}
    oddiy.update({"olma": 5})
    sanoq = Counter({"olma": 3})
    sanoq.update({"olma": 5})
    print(f"  dict.update:    {oddiy}")
    print(f"  Counter.update: {dict(sanoq)}")
    sanoq.update(["nok", "olma", "nok"])
    print(f"  iterable bilan: {dict(sanoq)}")

    print("\n=== 5. elements ===")
    print(f"  {sorted(Counter(a=2, b=1, c=0, d=-3).elements())}  ← 0 va manfiylar tashlandi")

    print("\n=== 6. So'z chastotasi ===")
    sozlar = re.findall(r"[^\W\d_]+(?:['ʻ‘’][^\W\d_]+)*", MATN.lower())
    chastota = Counter(sozlar)
    print(f"  so'zlar: {chastota.total()}, noyob: {len(chastota)}")
    for soz, son in chastota.most_common(4):
        print(f"  {soz:10} {'█' * son} {son}")
    print(f"  bir marta uchraganlar: {sum(1 for s in chastota.values() if s == 1)}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Yaratish ===
  satrdan:       Counter({'a': 5, 'b': 2, 'r': 2, 'k': 1, 'd': 1})
  ro'yxatdan:    Counter({'olma': 2, 'nok': 1})
  lug'atdan:     Counter({'olma': 3, 'nok': 1})
  kalit so'zdan: Counter({'olma': 3, 'nok': 1})

=== 2. Yo'q kalit ===
  c['z'] = 0, 'z' in c: False, len(c) = 5
  del c['z'] xato bermadi, len(c) = 5

=== 3. most_common va total ===
  most_common(2): [('a', 5), ('b', 2)]
  eng kami:       ('d', 1)
  total():        11 (satr uzunligi 11)
  teng sonlar — qo'shilish tartibida: [('a', 2), ('b', 1)] va [('b', 2), ('a', 1)]

=== 4. ⚠️ update: Counter qo'shadi, dict almashtiradi ===
  dict.update:    {'olma': 5}
  Counter.update: {'olma': 8}
  iterable bilan: {'olma': 9, 'nok': 2}

=== 5. elements ===
  ['a', 'a', 'b']  ← 0 va manfiylar tashlandi

=== 6. So'z chastotasi ===
  so'zlar: 25, noyob: 19
  kod        ███ 3
  python     ██ 2
  o'rganish  ██ 2
  va         ██ 2
  bir marta uchraganlar: 14

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — Counter arifmetikasi

python
"""+ - & | amallari; subtract va manfiy qiymatlar; unar + va -; <= bilan solishtirish; ombor va buyurtma; anagrammalar."""

from collections import Counter


def main() -> None:
    a = Counter(olma=3, nok=1)
    b = Counter(olma=1, nok=2, anor=1)

    print("=== 1. To'rt amal ===")
    print(f"  a = {dict(a)}, b = {dict(b)}")
    print(f"  a + b = {dict(a + b)}")
    print(f"  a - b = {dict(a - b)}  ← nok manfiy bo'lardi, tashlandi")
    print(f"  b - a = {dict(b - a)}")
    print(f"  a & b = {dict(a & b)}  (minimum)")
    print(f"  a | b = {dict(a | b)}  (maksimum)")

    print("\n=== 2. ⚠️ subtract manfiylarni saqlaydi ===")
    qoldiq = Counter(a)
    qoldiq.subtract(b)
    print(f"  subtract: {dict(qoldiq)}")
    print(f"  +qoldiq:  {dict(+qoldiq)}  (musbatlari)")
    print(f"  -qoldiq:  {dict(-qoldiq)}  (manfiylari, ishorasi almashgan)")

    print("\n=== 3. Solishtirish (3.10+) ===")
    print(f"  Counter(a=1) == Counter(a=1, b=0): {Counter(a=1) == Counter(a=1, b=0)}")
    print(f"  Counter(a=1) <= Counter(a=2, b=1): {Counter(a=1) <= Counter(a=2, b=1)}")
    print(f"  Counter(a=3) <= Counter(a=2, b=5): {Counter(a=3) <= Counter(a=2, b=5)}")

    print("\n=== 4. Ombor va buyurtmalar ===")
    ombor = Counter(daftar=10, ruchka=25, qalam=4, chizgich=0)
    buyurtmalar = [
        ("B-101", Counter(daftar=3, ruchka=5)),
        ("B-102", Counter(qalam=5, daftar=1)),
        ("B-103", Counter(chizgich=1)),
        ("B-104", Counter(ruchka=20, daftar=7)),
    ]
    for raqam, buyurtma in buyurtmalar:
        if buyurtma <= ombor:
            ombor -= buyurtma
            print(f"  ✅ {raqam} bajarildi, omborda: {dict(sorted(ombor.items()))}")
        else:
            print(f"  ❌ {raqam} yetishmaydi: {dict(buyurtma - ombor)}")
    print(f"  ⚠️ '-=' dan keyin 0 dona qolganlar ombordan yo'qoldi: {'chizgich' not in ombor and 'ruchka' not in ombor}")

    print("\n=== 5. Anagrammalar va harflar yetadimi ===")
    print(f"  'kitob' va 'botik' anagrammami: {Counter('kitob') == Counter('botik')}")
    xat = Counter("salom dunyo".replace(" ", ""))
    for soz in ("dunyo", "olma", "moll"):
        print(f"  gazeta harflaridan '{soz}' yig'iladimi: {Counter(soz) <= xat}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. To'rt amal ===
  a = {'olma': 3, 'nok': 1}, b = {'olma': 1, 'nok': 2, 'anor': 1}
  a + b = {'olma': 4, 'nok': 3, 'anor': 1}
  a - b = {'olma': 2}  ← nok manfiy bo'lardi, tashlandi
  b - a = {'nok': 1, 'anor': 1}
  a & b = {'olma': 1, 'nok': 1}  (minimum)
  a | b = {'olma': 3, 'nok': 2, 'anor': 1}  (maksimum)

=== 2. ⚠️ subtract manfiylarni saqlaydi ===
  subtract: {'olma': 2, 'nok': -1, 'anor': -1}
  +qoldiq:  {'olma': 2}  (musbatlari)
  -qoldiq:  {'nok': 1, 'anor': 1}  (manfiylari, ishorasi almashgan)

=== 3. Solishtirish (3.10+) ===
  Counter(a=1) == Counter(a=1, b=0): True
  Counter(a=1) <= Counter(a=2, b=1): True
  Counter(a=3) <= Counter(a=2, b=5): False

=== 4. Ombor va buyurtmalar ===
  ✅ B-101 bajarildi, omborda: {'daftar': 7, 'qalam': 4, 'ruchka': 20}
  ❌ B-102 yetishmaydi: {'qalam': 1}
  ❌ B-103 yetishmaydi: {'chizgich': 1}
  ✅ B-104 bajarildi, omborda: {'qalam': 4}
  ⚠️ '-=' dan keyin 0 dona qolganlar ombordan yo'qoldi: True

=== 5. Anagrammalar va harflar yetadimi ===
  'kitob' va 'botik' anagrammami: True
  gazeta harflaridan 'dunyo' yig'iladimi: True
  gazeta harflaridan 'olma' yig'iladimi: True
  gazeta harflaridan 'moll' yig'iladimi: False

Nima ko'rsatdi: 2.3-bo'lim.

Misol 3 — defaultdict va uning tuzoqlari

python
"""list/set/int bilan guruhlash; setdefault bilan solishtirish; o'qish kalit qo'shadi; get va in; tsikl ichida o'qish; default_factory; __missing__; ichma-ich daraxt; pickle."""

import json
import pickle
from collections import Counter, defaultdict

TALABALAR = [
    ("Aziz", "Python"), ("Malika", "Rust"), ("Aziz", "SQL"),
    ("Bek", "Python"), ("Malika", "Python"), ("Aziz", "Python"),
]


class Tarjima(dict):
    def __missing__(self, kalit: str) -> str:
        return f"[{kalit}]"


def nol() -> int:
    return 0


def main() -> None:
    print("=== 1. Guruhlash ===")
    kurslar: defaultdict[str, list[str]] = defaultdict(list)
    noyob: defaultdict[str, set[str]] = defaultdict(set)
    for ism, kurs in TALABALAR:
        kurslar[kurs].append(ism)
        noyob[ism].add(kurs)
    print(f"  kurs → talabalar: {dict(kurslar)}")
    print(f"  talaba → kurslar: {({k: sorted(v) for k, v in noyob.items()})}")

    setdefault_usul: dict[str, list[str]] = {}
    for ism, kurs in TALABALAR:
        setdefault_usul.setdefault(kurs, []).append(ism)
    print(f"  setdefault bilan bir xil: {setdefault_usul == kurslar}")

    print("\n=== 2. ⚠️ O'qish kalit qo'shadi ===")
    korishlar: defaultdict[str, int] = defaultdict(int)
    for sahifa in ["/", "/kurs", "/", "/blog"]:
        korishlar[sahifa] += 1
    print(f"  sanaldi: {dict(korishlar)}, len={len(korishlar)}")
    botlar = ["/wp-admin", "/.env", "/admin.php", "/phpmyadmin"]
    for manzil in botlar:
        if korishlar[manzil] > 0:
            pass
    print(f"  4 ta tekshiruvdan keyin len={len(korishlar)}: {sorted(korishlar)}")

    toza: defaultdict[str, int] = defaultdict(int, {"/": 2, "/kurs": 1})
    for manzil in botlar:
        if toza.get(manzil, 0) > 0 or manzil in toza:
            pass
    print(f"  get va in bilan len={len(toza)}  ← hech narsa qo'shilmadi")
    c = Counter({"/": 2})
    _ = c["/wp-admin"]
    print(f"  Counter o'qishda qo'shmaydi: len={len(c)}")

    print("\n=== 3. ⚠️ Tsikl ichida o'qish ===")
    ota: defaultdict[str, list[str]] = defaultdict(list, {"Aziz": ["Bek"], "Bek": []})
    try:
        for ism in ota:
            for bola in ota[ism]:
                _ = ota[bola + "ning bolasi"]
    except RuntimeError as xato:
        print(f"  RuntimeError: {xato}")

    print("\n=== 4. default_factory va __missing__ ===")
    d: defaultdict[str, list[int]] = defaultdict(list, {"a": [1]})
    print(f"  default_factory: {d.default_factory.__name__}")
    d.default_factory = None
    try:
        d["z"]
    except KeyError:
        print("  default_factory=None → KeyError")
    tarjima = Tarjima(salom="hello")
    print(f"  Tarjima: {tarjima['salom']}, {tarjima['rahmat']}, get: {tarjima.get('rahmat')}, len={len(tarjima)}")

    print("\n=== 5. Ichma-ich daraxt va JSON ===")
    def daraxt() -> defaultdict:
        return defaultdict(daraxt)

    manzillar = daraxt()
    manzillar["Toshkent"]["Chilonzor"]["9-kvartal"] = 120
    manzillar["Toshkent"]["Yunusobod"]["4-kvartal"] = 85
    manzillar["Samarqand"]["Markaz"]["Registon"] = 40
    print(f"  {json.dumps(manzillar, ensure_ascii=False)}")
    qaytgan = json.loads(json.dumps(Counter("aab")))
    print(f"  JSON dan qaytgan tur: {type(qaytgan).__name__}")

    print("\n=== 6. pickle ===")
    for nom, obyekt in (("defaultdict(int)", defaultdict(int)),
                        ("defaultdict(nol)", defaultdict(nol)),
                        ("defaultdict(lambda: 0)", defaultdict(lambda: 0))):
        try:
            pickle.dumps(obyekt)
            print(f"  ✅ {nom}")
        except (pickle.PicklingError, AttributeError) as xato:
            print(f"  ❌ {nom}: {type(xato).__name__}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Guruhlash ===
  kurs → talabalar: {'Python': ['Aziz', 'Bek', 'Malika', 'Aziz'], 'Rust': ['Malika'], 'SQL': ['Aziz']}
  talaba → kurslar: {'Aziz': ['Python', 'SQL'], 'Malika': ['Python', 'Rust'], 'Bek': ['Python']}
  setdefault bilan bir xil: True

=== 2. ⚠️ O'qish kalit qo'shadi ===
  sanaldi: {'/': 2, '/kurs': 1, '/blog': 1}, len=3
  4 ta tekshiruvdan keyin len=7: ['/', '/.env', '/admin.php', '/blog', '/kurs', '/phpmyadmin', '/wp-admin']
  get va in bilan len=2  ← hech narsa qo'shilmadi
  Counter o'qishda qo'shmaydi: len=1

=== 3. ⚠️ Tsikl ichida o'qish ===
  RuntimeError: dictionary changed size during iteration

=== 4. default_factory va __missing__ ===
  default_factory: list
  default_factory=None → KeyError
  Tarjima: hello, [rahmat], get: None, len=1

=== 5. Ichma-ich daraxt va JSON ===
  {"Toshkent": {"Chilonzor": {"9-kvartal": 120}, "Yunusobod": {"4-kvartal": 85}}, "Samarqand": {"Markaz": {"Registon": 40}}}
  JSON dan qaytgan tur: dict

=== 6. pickle ===
  ✅ defaultdict(int)
  ✅ defaultdict(nol)
  ❌ defaultdict(lambda: 0): PicklingError

Nima ko'rsatdi: 2.4–2.7-bo'limlar.

Misol 4 — Amaliy: onlayn do'kon buyurtmalari tahlili

Bitta hafta buyurtmalari asosida hisobot tuzamiz: eng ko'p sotilganlar, toifalar bo'yicha tushum, har mijozning toifalari, qaysi mahsulotlar birga olinadi va omborni to'ldirish uchun nima buyurtma qilish kerak. Pul butun so'mlarda saqlanadi — suzuvchi nuqta xatolarisiz.

python
"""Counter bilan sotuvlar va juftliklar; defaultdict bilan tushum va mijoz toifalari; ombor yetishmovchiligi; natijalarni o'zaro tekshirish."""

from collections import Counter, defaultdict
from itertools import combinations

NARXLAR = {"daftar": 8_000, "ruchka": 3_500, "sumka": 145_000, "kitob": 65_000, "kalkulyator": 120_000}
TOIFALAR = {"daftar": "kanselyariya", "ruchka": "kanselyariya", "sumka": "aksessuar",
            "kitob": "kitoblar", "kalkulyator": "texnika"}

BUYURTMALAR = [
    ("Aziz", {"daftar": 5, "ruchka": 10}),
    ("Malika", {"kitob": 2, "daftar": 2}),
    ("Bek", {"sumka": 1, "daftar": 3, "ruchka": 2}),
    ("Aziz", {"kalkulyator": 1}),
    ("Nodira", {"daftar": 4, "ruchka": 4, "kitob": 1}),
    ("Malika", {"sumka": 1}),
    ("Bek", {"ruchka": 6}),
]

OMBOR = Counter(daftar=10, ruchka=30, sumka=1, kitob=5, kalkulyator=2)
ZAXIRA_MINIMUM = 5


def som(summa: int) -> str:
    return f"{summa:,} so'm".replace(",", " ")


def main() -> None:
    sotuv: Counter[str] = Counter()
    tushum: defaultdict[str, int] = defaultdict(int)
    mijoz_toifalari: defaultdict[str, set[str]] = defaultdict(set)
    mijoz_summasi: Counter[str] = Counter()
    juftliklar: Counter[tuple[str, str]] = Counter()

    for mijoz, savat in BUYURTMALAR:
        sotuv.update(savat)
        for mahsulot, son in savat.items():
            summa = NARXLAR[mahsulot] * son
            tushum[TOIFALAR[mahsulot]] += summa
            mijoz_summasi[mijoz] += summa
            mijoz_toifalari[mijoz].add(TOIFALAR[mahsulot])
        juftliklar.update(combinations(sorted(savat), 2))

    print("=== 1. Eng ko'p sotilganlar ===")
    for mahsulot, son in sotuv.most_common(3):
        print(f"  {mahsulot:12} {son:>3} dona")
    print(f"  jami dona: {sotuv.total()}")

    print("\n=== 2. Toifalar bo'yicha tushum ===")
    for toifa, summa in sorted(tushum.items(), key=lambda juft: -juft[1]):
        print(f"  {toifa:14} {som(summa):>13}")
    jami = sum(tushum.values())
    print(f"  jami tushum: {som(jami)}")

    print("\n=== 3. Mijozlar ===")
    for mijoz, summa in mijoz_summasi.most_common():
        print(f"  {mijoz:7} {som(summa):>13}  toifalar: {sorted(mijoz_toifalari[mijoz])}")

    print("\n=== 4. Birga olinadigan mahsulotlar ===")
    for (x, y), son in juftliklar.most_common(2):
        print(f"  {x} + {y}: {son} ta buyurtmada")

    print("\n=== 5. Ombor ===")
    qoldiq = Counter(OMBOR)
    qoldiq.subtract(sotuv)
    kamomad = -qoldiq
    print(f"  sotuvdan keyingi qoldiq: {dict(qoldiq)}")
    print(f"  omborda yetishmagan (oldindan buyurtma): {dict(kamomad)}")
    maqsad = Counter({m: ZAXIRA_MINIMUM for m in NARXLAR})
    toldirish = maqsad - +qoldiq
    print(f"  har biridan kamida {ZAXIRA_MINIMUM} ta bo'lishi uchun buyurtma: {dict(toldirish)}")

    print("\n=== 6. O'zaro tekshiruvlar ===")
    tekshir_jami = sum(NARXLAR[m] * s for m, s in sotuv.items())
    print(f"  toifalar tushumi = mahsulotlar tushumi: {jami == tekshir_jami}")
    print(f"  mijozlar summasi = jami tushum: {mijoz_summasi.total() == jami}")
    print(f"  sotilgan dona = buyurtmalardagi dona: {sotuv.total() == sum(sum(s.values()) for _, s in BUYURTMALAR)}")
    print(f"  har mahsulotda qoldiq + sotuv = boshlang'ich ombor: {all(qoldiq[m] + sotuv[m] == OMBOR[m] for m in NARXLAR)}")
    print(f"  ⚠️ OMBOR - sotuv da yetishmovchilik ko'rinmaydi: {dict(OMBOR - sotuv)}")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Eng ko'p sotilganlar ===
  ruchka        22 dona
  daftar        14 dona
  kitob          3 dona
  jami dona: 42

=== 2. Toifalar bo'yicha tushum ===
  aksessuar       290 000 so'm
  kitoblar        195 000 so'm
  kanselyariya    189 000 so'm
  texnika         120 000 so'm
  jami tushum: 794 000 so'm

=== 3. Mijozlar ===
  Malika   291 000 so'm  toifalar: ['aksessuar', 'kanselyariya', 'kitoblar']
  Bek      197 000 so'm  toifalar: ['aksessuar', 'kanselyariya']
  Aziz     195 000 so'm  toifalar: ['kanselyariya', 'texnika']
  Nodira   111 000 so'm  toifalar: ['kanselyariya', 'kitoblar']

=== 4. Birga olinadigan mahsulotlar ===
  daftar + ruchka: 3 ta buyurtmada
  daftar + kitob: 2 ta buyurtmada

=== 5. Ombor ===
  sotuvdan keyingi qoldiq: {'daftar': -4, 'ruchka': 8, 'sumka': -1, 'kitob': 2, 'kalkulyator': 1}
  omborda yetishmagan (oldindan buyurtma): {'daftar': 4, 'sumka': 1}
  har biridan kamida 5 ta bo'lishi uchun buyurtma: {'daftar': 5, 'sumka': 5, 'kitob': 3, 'kalkulyator': 4}

=== 6. O'zaro tekshiruvlar ===
  toifalar tushumi = mahsulotlar tushumi: True
  mijozlar summasi = jami tushum: True
  sotilgan dona = buyurtmalardagi dona: True
  har mahsulotda qoldiq + sotuv = boshlang'ich ombor: True
  ⚠️ OMBOR - sotuv da yetishmovchilik ko'rinmaydi: {'ruchka': 8, 'kitob': 2, 'kalkulyator': 1}

Nima ko'rsatdi: 2.1–2.5-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Counter.update — dict.update bilan bir xil" Qo'shadi, almashtirmaydi
"Counter da yo'q kalit KeyError" 0 qaytaradi, qo'shmaydi
"a - b manfiy natijalarni saqlaydi" Tashlaydi; subtract saqlaydi
"defaultdict da d[k] faqat o'qiydi" Kalitni qo'shadi
"defaultdict.get(k) standart qiymat beradi" None qaytaradi
"__missing__ get da ham ishlaydi" Faqat d[k] da
"json.loads Counter ni qaytaradi" Oddiy dict
"Istalgan defaultdict pickle bo'ladi" lambda fabrika bilan — yo'q

6. Keng tarqalgan xatolar va yechimlari

1. Tekshiruvda d[k]

python
if sanoq[manzil] > 0: ...              # ❌ defaultdict — kalit qo'shiladi
if sanoq.get(manzil, 0) > 0: ...       # ✅

2. Counter bilan dict.update ni aralashtirish

python
jami = Counter(); jami.update(kunlik)  # ✅ qo'shiladi
jami = {}; jami.update(kunlik)         # ❌ almashtiriladi

3. Manfiy qoldiqni - bilan yo'qotish

python
qoldiq = ombor - sotuv                 # ❌ yetishmovchilik ko'rinmaydi
qoldiq = Counter(ombor); qoldiq.subtract(sotuv)   # ✅

4. Qo'lda sanash

python
d = {}
for x in xs: d[x] = d.get(x, 0) + 1    # ⚠️ ishlaydi, lekin
d = Counter(xs)                        # ✅ qisqa va C da tezlashtirilgan

5. defaultdict ni tashqariga berish

python
return guruhlar                        # ⚠️ chaqiruvchi o'qib kalit qo'shishi mumkin
return dict(guruhlar)                  # ✅

6. Takrorlarsiz guruhda list

python
guruh = defaultdict(list)              # ⚠️ takrorlar
guruh = defaultdict(set)               # ✅

7. pickle qilinadigan joyda lambda

python
defaultdict(lambda: "noma'lum")        # ❌ multiprocessing, kesh
def nomalum(): return "noma'lum"
defaultdict(nomalum)                   # ✅

8. most_common tartibiga tayanish

python
top = c.most_common(1)[0]              # ⚠️ teng sonlarda — qo'shilish tartibi
top = max(c.items(), key=lambda kv: (kv[1], kv[0]))   # ✅ aniq qoida

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 6-qism (o'tilgan): dict, set, comprehension
  • 8.14-dars (o'tilgan): konteyner protokoli va dunder metodlar
  • 15.7-dars: deque, OrderedDict
  • 15.8–15.9-darslar: itertools.groupby, combinations
  • 16-qism: fayllardan statistika yig'ish
  • 24-qism: pandas value_counts va groupby — xuddi shu g'oyalar
  • 26.4-dars: scraping — sahifalardan so'z chastotalari

8. Eng yaxshi amaliyotlar

  1. Sanash — Counter, guruhlash — defaultdict(list) yoki defaultdict(set).

  2. defaultdict da tekshiruv — get yoki in.

  3. Tashqariga dict(...) qaytaring.

  4. Yetishmovchilikni ko'rish kerak bo'lsa — subtract.

  5. Teng sonlarda aniq tartib kerak bo'lsa — o'z saralash kalitingiz.

  6. Pul — butun sonlarda (so'm yoki tiyin).

  7. pickle qilinadigan joyda fabrika — nomli funksiya.

  8. Natijalarni o'zaro tekshiring — jami tushum har xil yo'l bilan bir xil chiqishi kerak.


9. Amaliy topshiriq

Vazifa 1: Natijani bashorat qiling

python
from collections import Counter, defaultdict
1.  print(Counter("banana")["a"])
2.  print(Counter("banana").most_common(1))
3.  c = Counter("ab"); c["z"]; print(len(c))
4.  d = defaultdict(int); d["z"]; print(len(d))
5.  print(Counter(a=3) - Counter(a=5))
6.  c = Counter(a=3); c.subtract(Counter(a=5)); print(c["a"])
7.  c = Counter(a=1); c.update({"a": 2}); print(c["a"])
8.  print(Counter(a=2, b=1) & Counter(a=1, b=3))
9.  print(Counter("ab") == Counter("ba"))
10. d = defaultdict(list); print(d.get("x"))
11. print(sorted(Counter(x=2, y=0).elements()))
12. print(Counter("aaabb").total())
Javoblar
  1. 3
  2. [('a', 3)]
  3. 2 — Counter o'qishda qo'shmaydi
  4. 1 — defaultdict qo'shadi
  5. Counter() — manfiy tashlandi
  6. -2
  7. 3
  8. Counter({'a': 1, 'b': 1})
  9. True
  10. None
  11. ['x', 'x']
  12. 5

Vazifa 2: Xatolarni tuzating

python
1.  def korilganmi(sanoq, sahifa):              # sanoq — defaultdict(int)
        return sanoq[sahifa] > 0

2.  def birlashtir(kunlik_hisobotlar):
        jami = {}
        for hisobot in kunlik_hisobotlar:
            jami.update(hisobot)
        return jami

3.  def yetishmaydi(ombor, buyurtma):
        qoldiq = ombor - buyurtma
        return {k: v for k, v in qoldiq.items() if v < 0}

4.  def guruhla(juftliklar):
        g = {}
        for kalit, qiymat in juftliklar:
            g[kalit].append(qiymat)
        return g

5.  KESH = defaultdict(lambda: None)             # multiprocessing orqali yuboriladi
Javoblar
python
1.  def korilganmi(sanoq, sahifa):
        return sanoq.get(sahifa, 0) > 0

2.  def birlashtir(kunlik_hisobotlar):
        jami = Counter()
        for hisobot in kunlik_hisobotlar:
            jami.update(hisobot)
        return dict(jami)

3.  def yetishmaydi(ombor, buyurtma):
        return dict(buyurtma - ombor)            # yoki subtract + manfiylari

4.  def guruhla(juftliklar):
        g = defaultdict(list)
        for kalit, qiymat in juftliklar:
            g[kalit].append(qiymat)
        return dict(g)

5.  def yoq():
        return None
    KESH = defaultdict(yoq)

Vazifa 3: Jurnal statistikasi

Veb-server jurnali (15.2-dars formati) uchun:

  1. Eng ko'p so'ralgan 10 ta manzil (Counter)
  2. Har IP → so'ragan manzillar to'plami (defaultdict(set))
  3. Soatlar bo'yicha so'rovlar (Counter va datetime)
  4. 404 bergan manzillar va ularni so'ragan IP lar
  5. Hech bir natija lug'atida "sun'iy" kalit paydo bo'lmaganini testda isbotlang

Vazifa 4: Anagramma guruhlari

  1. O'zbekcha so'zlar ro'yxatidan anagramma guruhlarini toping (defaultdict(list), kalit — saralangan harflar)
  2. Xuddi shuni Counter kaliti bilan qilishga urinib ko'ring — nega ishlamaydi? (frozenset(Counter(...).items()) yechimi)
  3. Eng katta guruhni toping
  4. Apostrofli so'zlarni (o'rik) to'g'ri qayta ishlang

Vazifa 5: Ovoz berish tizimi

  1. Ovozlar ro'yxati — Counter
  2. Teng ovoz olganlarda — alifbo tartibida g'olib (aniq qoida)
  3. "Ikkinchi tur": eng kam ovoz olgan nomzodni chiqarib, uning saylovchilarining ikkinchi tanlovini qo'shing (reytingli ovoz)
  4. Har turdagi natijani chop eting

Vazifa 6: Retsept va xarid ro'yxati

  1. Har retsept — Counter (ingredient → gramm)
  2. Haftalik menyu: Counter lar yig'indisi
  3. Uydagi mahsulotlar — Counter; xarid ro'yxati = menyu - uyda
  4. Qaysi retseptlarni hozir tayyorlash mumkin (retsept <= uyda)
  5. Birliklarni (kg/g) me'yorlash

Vazifa 7: O'ylash

Counter — dict ning vorisi, shuning uchun Counter kutilgan joyga dict ham, dict kutilgan joyga Counter ham berilishi mumkin. Lekin update ning ma'nosi ular orasida teskari. Bu Liskov almashtirish tamoyilini (8-qism) buzadimi? Standart kutubxona nega bunday dizaynni tanlagan?

Javob

Qisqa javob: Ha, qat'iy ma'noda Counter.update Liskov tamoyilini buzadi: dict uchun yozilgan va update "almashtiradi" deb hisoblagan kod Counter bilan boshqacha natija beradi. Standart kutubxona bu yerda amaliy qulaylikni tanlagan: Counter ning asosiy vazifasi — sanash, va sanashda update ning tabiiy ma'nosi — qo'shish. Bu kelishuv hujjatlashtirilgan, lekin xavfli joy bo'lib qoladi.

1. Liskov almashtirish tamoyili

"Agar S — T ning vorisi bo'lsa, T kutilgan har joyda S ni ishlatish dastur to'g'riligini buzmasligi kerak."

2. Counter qayerda buzadi

Metod dict shartnomasi Counter
update(m) Qiymatlar almashtiriladi Qiymatlar qo'shiladi
d[yoq] KeyError 0
fromkeys Ishlaydi NotImplementedError
== Kalitlar to'plami bir xil bo'lishi kerak 0 qiymatli kalitlar e'tiborsiz (3.10+)
python
def sozlamani_yangila(sozlama: dict, yangi: dict) -> None:
    sozlama.update(yangi)            # "almashtiradi" deb yozilgan

s = Counter(port=8000)
sozlamani_yangila(s, {"port": 8080})
s["port"]                            # 16080 ❌

3. Nega shunday tanlangan

  1. Tarix: Counter Python 2.7/3.1 da (2009) boshqa tillardagi "bag/multiset" dan ilhomlangan; o'sha paytda merosdan foydalanish keng tarqalgan edi
  2. Qulaylik: dict ning barcha metodlari (items, keys, iteratsiya, JSON) tekin keladi
  3. Tezlik: C da tezlashtirilgan dict ustida qurilgan
  4. Tabiiy ma'no: sanovchida "yangilash" — "qo'shish"

4. Muqobil dizaynlar

Dizayn Afzalligi Kamchiligi
Meros (Counter(dict)) — hozirgi Qulay, tez, dict API LSP buziladi
Kompozitsiya (ichida dict) Shartnoma aniq Ko'p kod, isinstance(c, dict) — False
Mapping protokoli (13.5) Faqat o'qish shartnomasi Yozish uchun alohida API
Boshqa nom (add yoki count_all) Chalkashlik yo'q update — tanish nom

5. Amaliy xulosa

  1. Funksiya dict qabul qilsa va update qilsa — Counter bermang (yoki dict(c) bering)
  2. Tur annotatsiyasida o'qish uchun Mapping[str, int] ishlating — update chaqirilmasligini bildiradi
  3. O'z sinflaringizda meros o'rniga kompozitsiya yoki protokollarni afzal ko'ring (8-qism, 13.5-dars)
  4. Standart kutubxona ham "ideal" OOP ga emas, amaliyotga moslashgan — hujjatni o'qish muhim

Nimani mustahkamlaydi: 2.1–2.7-bo'limlar.


Xulosa

Bu darsda sanash va guruhlash uchun maxsus lug'at turlarini o'rgandik.

Eng muhim uch fikr:

  1. Counter — sanovchi va multitoʻplam. Yo'q kalit uchun 0 qaytaradi va uni qo'shmaydi, most_common eng ko'plarini (teng sonlarda qo'shilish tartibida), total() yig'indini beradi. update qo'shadi — dict.update kabi almashtirmaydi. Arifmetika (+ - & |) manfiy va nol natijalarni tashlaydi, subtract esa saqlaydi; buyurtma <= ombor bitta ifoda bilan "bajarib bo'ladimi?" savoliga javob beradi.

  2. defaultdict — guruhlashning asosi. defaultdict(list), defaultdict(set), defaultdict(int) bilan if kalit not in d andozasi yo'qoladi. Fabrika — argumentsiz chaqiriladigan istalgan narsa; pickle kerak bo'lsa lambda emas, nomli funksiya.

  3. defaultdict da o'qish yozadi. d[kalit] yo'q kalitni qo'shadi — tekshiruvlarda xotira o'sadi, len noto'g'ri bo'ladi, tsikl ichida RuntimeError chiqadi. Tekshiruv uchun get yoki in, tashqariga esa dict(d). Mexanizm — __missing__: u faqat d[k] da ishlaydi, get va in da emas.

Keyingi darsda collections ning qolgan turlarini ko'ramiz: deque — ikki tomonlama navbat, OrderedDict, ChainMap va namedtuple.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
15.6-dars: collections: Counter, defaultdict — IlmHamroh