Shanraq.org Shanraq.org
JSON: бірінші сабақта сеніммен талдаған API жауабы
IT

Python: деректен өз есебіңізге дейін 13-сабақ (барлығы 56)

JSON: бірінші сабақта сеніммен талдаған API жауабы

Python курсының он үшінші сабағы. Дүниежүзілік банктің жауабы — екі элементтен тұратын тізім: қызметтік бөлік пен жазбалар. Оны қолмен талдап, есепті үнсіз бұзатын нәрсені табамыз: `null` `None` болады, ал сан-кілттер JSON арқылы айналып келгенде жол болып оралады — `2025` кетеді, `"2025"` келеді.

Не үшін керек

Бірінші сабақта бағдарлама желіге барып, жауап алды да, одан сандарды шығарды. Сонда біз «ештеңе түсінбей іске қосыңыз» дедік. Бүгін түсінеміз.

JSON — API-лердің дерлік бәрі сөйлейтін тіл: банк те, ауа райы да, валюта бағамы да, тілдік модель де. Пішім қарапайым: сөздіктер, тізімдер, жолдар, сандар, true, false, null. Дәл осы алты нәрсе.

Қарапайымдығы алдамшы. JSON-нан Python-ға және кері аударудың екі орны деректі үнсіз бұзады, әрі екеуі де біздің жауапта көрінеді.

Бірден тұтас

json_demo.py файлы. Іске қосу: ортадан python json_demo.py.

Міндеттісі — алғашқы екі блок: жауапты талдау және одан қатарды шығару. Үшінші мен төртіншісі сабақ жазылған тұзақтарды көрсетеді.

"""13-сабақ: JSON — сан болғанға дейінгі, желіден келген нәрсе.

Бұл — Дүниежүзілік банктің нағыз жауабының бір бөлігі: жоғарыда қызметтік
бөлік, төменде жазбалар. Бірінші сабақта бағдарлама оны түсіндірмей талдаған
еді. Енді талдайық.

Жауап үш жазбаға дейін қысқартылған, ал 2023 жылдың мәні null-ге ауыстырылған:
нағыз жауапта ол бар, бірақ бүгін бізге олқылық керек, әрі оның әдейі
қойылғанын айтқан адал.
"""

import json

ANSWER = """[
  {"page": 1, "pages": 1, "per_page": 3, "total": 3},
  [
    {"country": {"id": "KZ", "value": "Kazakhstan"}, "date": "2025", "value": 11.39},
    {"country": {"id": "KZ", "value": "Kazakhstan"}, "date": "2024", "value": 8.69},
    {"country": {"id": "KZ", "value": "Kazakhstan"}, "date": "2023", "value": null}
  ]
]"""

data = json.loads(ANSWER)

print("== келген жауап")
print("жоғарғы деңгей:", type(data).__name__, "—", len(data), "элемент")
print("қызметтік бөлік:", data[0])
print("жазба саны:", len(data[1]))

print()
print("== жазбаларды талдаймыз")
series = {}
for row in data[1]:
    year = int(row["date"])
    series[year] = row["value"]
    print(f"{row['country']['value']} {year}: {row['value']}")

print()
print("== JSON арқылы айналғаннан кейінгі кілттер")
text = json.dumps(series)
back = json.loads(text)
print("дейін: ", series)
print("мәтін: ", text)
print("кейін: ", back)

print()
print("== кириллица және шегіністер")
note = {"note": "дерек жоқ"}
print("әдепкіде:      ", json.dumps(note))
print("ensure_ascii=False:", json.dumps(note, ensure_ascii=False))
print("байт:", len(json.dumps(note).encode()), "экрандаумен,",
      len(json.dumps(note, ensure_ascii=False).encode()), "онсыз")
print(json.dumps(series, ensure_ascii=False, indent=2))

Шығатыны:

== келген жауап
жоғарғы деңгей: list — 2 элемент
қызметтік бөлік: {'page': 1, 'pages': 1, 'per_page': 3, 'total': 3}
жазба саны: 3

== жазбаларды талдаймыз
Kazakhstan 2025: 11.39
Kazakhstan 2024: 8.69
Kazakhstan 2023: None

== JSON арқылы айналғаннан кейінгі кілттер
дейін:  {2025: 11.39, 2024: 8.69, 2023: None}
мәтін:  {"2025": 11.39, "2024": 8.69, "2023": null}
кейін:  {'2025': 11.39, '2024': 8.69, '2023': None}

== кириллица және шегіністер
әдепкіде:       {"note": "\u0434\u0435\u0440\u0435\u043a \u0436\u043e\u049b"}
ensure_ascii=False: {"note": "дерек жоқ"}
байт: 61 экрандаумен, 29 онсыз
{
  "2025": 11.39,
  "2024": 8.69,
  "2023": null
}

Талдау

JSON құралатын алты нәрсе

{...} объектісі сөздікке, [...] массиві тізімге, жол жолға, сан int не float-қа, true/false True/False-ке, null None-ға айналады. Пішімде бұдан басқа ештеңе жоқ: күн де, кортеж де, жиын да.

Осыдан бірінші тәжірибелік қорытынды: JSON-дағы күн — бұл жол. Біздің жауапта жыл "2025" болып келеді, әрі талдаудағы int(row["date"]) артық сақтықтан тұрған жоқ.

Елестетіп көріңіз. Тізімдемесі бар сәлемдеме. Қорапта алты-ақ түрлі зат жата алады, әрі тізімдеме оларды атымен санайды. Қалғанының бәрі — ваза да, мысық та, уәде де — осы алтаудың біріне оралады немесе мүлде жүрмейді.

loads пен load — бір әріп, әртүрлі кіріс

json.loads(text) жолды талдайды, json.load(file) — ашық файлды. Кері жағы да солай: dumps жол береді, dump файлға жазады. Соңындағы s әрпі — «string» дегеннен, айырма да сол ғана.

Бірінші сабақта json.load(answer) тұрған, онда answer — файл сияқты ұстайтын желі жауабы. Бүгін бізде бағдарлама ішіндегі жол, сондықтан loads.

Қызметтік бөлік пен жазбалар

жоғарғы деңгей: list — 2 элемент

Дүниежүзілік банк тізіммен жауап береді: нөлінші элемент — барлығы қанша жазба мен бет бар, біріншісі — жазбалардың өзі. Бұл JSON-ның жалпы ережесі емес, нақты API-дің келісімі; ауа райында не Ұлттық банкте ол басқаша болады.

Осыдан бір кешті үнемдейтін әдет шығады: алдымен не келгенін басып шығарыңыз. type(), len(), бірінші жазбаның кілттері — үш жол, содан кейін жауаптың пішіні көрінеді. Құжаттамадан болжау ұзағырақ.

Кірістірілгенді бір қадаммен талдайды: row["country"]["value"] — алдымен елдің сөздігі, содан кейін оның аты. Кілт болмауы мүмкін болса, .get алады: row.get("unit", "") KeyError орнына бос жол қайтарады.

null — бұл None, нөл емес

Жауапта 2023 жылдың орнында null тұр, ал Python-да ол None болады. Бұл — жетінші сабақтағы сол жетіспейтін мән: нөл де емес, бос жол да емес — мәннің болмауы.

Тексеру де сол: if value is None. Қате де сол: жетіспейтін мәнді нөл деп санап, нағызынан төмен орташа алу.

Сан-кілттер жол болып оралады

Сабақтың ең қымбат орны:

дейін:  {2025: 11.39, 2024: 8.69, 2023: None}
мәтін:  {"2025": 11.39, "2024": 8.69, "2023": null}
кейін:  {'2025': 11.39, '2024': 8.69, '2023': None}

Кілттер сан болып кетті, жол болып оралды. Пішімнің өзі солай құрылған: JSON-дағы объект кілті — әрқашан жол, әрі json.dumps 2025-ті үнсіз "2025"-ке айналдырады.

Әрі қарай бағдарламада series[2025] KeyError береді, ал дерек орнында, көзбен бәрі көрініп тұр. Жамаумен емес, шешіммен емделеді: не кілттерді оқығанда кері келтіреді ({int(k): v for k, v in ...} — біздегі int(row["date"]) сияқты), не жылды кілтте емес, жазбаның ішінде сақтайды.

Кириллица және шегіністер

әдепкіде:       {"note": "\u0434\u0435\u0440\u0435\u043a \u0436\u043e\u049b"}
ensure_ascii=False: {"note": "дерек жоқ"}
байт: 61 экрандаумен, 29 онсыз

Әдепкіде json.dumps латыннан басқаның бәрін escape-тізбектермен жазады. Файл дұрыс JSON болып қала береді, оны кез келген бағдарлама дұрыс оқиды — бірақ адам онда ештеңе көрмейді.

Көзбен ашатын файлдар үшін ensure_ascii=False жазады, оқуға ыңғайлы болу үшін indent=2 қосады.

Әдепкі ұзындық үшін таңдалған жоқ: экрандаумен біздің жол 61 байт, онсыз — 29, әрі мұны бағдарлама өлшеп берді. Бір кирилл әрпі екі байт UTF-8-дің орнына алты таңбалық \uXXXX тұрады. Әдепкінің ұтысы басқада — таза ASCII кодтауды бұрмалайтын арнадан да өтеді. Ал үнемдеу бос орындарда: separators=(",", ":") json.dumps үтір мен қос нүктеден кейін қоятындарын алып тастайды.

Сабақ картасы

Сабақ картасы: жауаптың пішіні, null және кілттер

Өз сөзіңізбен айтыңыз

Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптары — сабақтың соңында.

  1. json.loads json.load-тан немен өзгеше?
  2. JSON-ға жазып, кері оқығаннан кейін series[2025] неге жұмыс істемей қалады?
  3. Python-да null орнына не келеді, әрі оның нөлден айырмасы неде?

Жаттығу

Тапсырмаға дейінгі үш қысқа қадам: болжау, толықтыру, жөндеу. Жауаптары — сабақтың соңында, бірақ алдымен өзіңіз жауап беріңіз.

1. Болжаңыз. Бұл бағдарлама не басып шығарады?

import json

data = json.loads('{"year": "2025", "value": null}')
print(type(data["year"]).__name__, data["value"])

2. Бос орынды толтырыңыз. ... орнына кілттерге сандық түрін қайтаратын нәрсені қойыңыз.

import json

series = {2025: 11.39, 2024: 8.69}
back = json.loads(json.dumps(series))
fixed = {}
for key, value in back.items():
    fixed[...] = value
print(fixed)

3. Жөндеңіз. Дерек орнында тұрса да, бағдарлама KeyError-мен құлайды. Кілттің қайда кеткенін түсіндіріп, оқуды жөндеңіз.

import json

series = {2025: 11.39}
back = json.loads(json.dumps(series))
print(back[2025])

Тапсырма

Міндетті. Берілгені:

ANSWER = """[
  {"page": 1, "pages": 1, "per_page": 4, "total": 4},
  [
    {"country": {"id": "KZ", "value": "Kazakhstan"}, "date": "2025", "value": 11.39},
    {"country": {"id": "KZ", "value": "Kazakhstan"}, "date": "2024", "value": 8.69},
    {"country": {"id": "KZ", "value": "Kazakhstan"}, "date": "2023", "value": null},
    {"country": {"id": "KZ", "value": "Kazakhstan"}, "date": "2022", "value": 15.0}
  ]
]"""

Жауапты талдап, ішінде неше жазба бар екенін басып шығарыңыз. «Жыл → мән» сөздігін жинаңыз: жыл — сан, ал null тұрған жазбалар жетіспейтін мәндер тізіміне кетеді. Саны бар жыл нешеу екенін және солар бойынша екі таңбалы орташаны, содан кейін жетіспейтін мәндер тізімін басып шығарыңыз. Содан соң JSON арқылы бір айналым жасаңыз — dumps, кері loads — да, кілттің типін дейін және кейін басып шығарыңыз, артынан кілттерді жөндеп, 2025 жылдың мәнін басыңыз.

Күтілетін шығу:

жазба саны: 4
саны бар жыл: 3, орташа: 11.69
олқылықтар: [2023]
кілт дейін: int | кейін: str
жөндегеннен кейін: 11.39

Дайын болғаны: шығу жол-жолмен сәйкес келеді; жыл талдау кезінде санға айналады, кейін емес; null орташаға емес, жетіспейтін мәндерге түскен; JSON арқылы айналымнан кейін санмен жүгіну қайта жұмыс істейді.

Өз деректеріңізде. Ішінде бір None бар «жыл → мән» сөздігіңізді алыңыз. Оны json.dump арқылы ensure_ascii=False және indent=2-мен файлға жазыңыз, json.load арқылы кері оқыңыз да, кілттердің типтерін дейін және кейін басып шығарыңыз. Содан кейін оқуды кілттер қайта сан болатындай етіп жөндеңіз.

Қалауыңызша.

  • Бірінші сабақтағы нағыз жауапты талдап, бірінші жазбаның кілттерін басып шығарыңыз.
  • {1, 2} жиынына json.dumps қолданып көріңіз де, қатені оқыңыз.
  • Бір сөздікті ensure_ascii=False-пен және онсыз сақтап, файлдардың көлемін салыстырыңыз.

Жобада бұл қайда тұрады

Шолу жауапқа сеніп қалуын қояды. Пішімі тексеріледі, null жетіспейтін мәндерге түседі, ал жыл талдау кезінде санға айналады, кейін емес — өйткені кейін кеш.

Қарыздар. Жауапты қолмен талдап, кілттер орнында деп үміттеніп отырмыз. Пішімнің нағыз тексеруі — «мұнда сан күтілген, жол келді» деп айтатын схема; оған шолуға тілдік модельдің жауабы келгенде жетеміз.

Жауаптар

Жауаптарды көрсету

Сұрақтарға

  1. Тек кірісімен: loads жолды талдайды, load — ашық файлды. s әрпі — «string» дегеннен. Дәл солай dumps жол береді, ал dump файлға жазады.
  2. Өйткені JSON-дағы объект кілті — әрқашан жол, әрі жазғанда 2025 "2025"-ке айналады. Оқығаннан кейін кілттер жол болып қалады, ал санмен жүгіну KeyError береді.
  3. None келеді — мәннің болмауы. Нөл — бұл мән, әрі оларды шатастыруға болмайды: нөл деп саналған жетіспейтін мән орташаны төмендетеді.

Жаттығуға

  1. str None. JSON-дағы күн — жол, сондықтан "2025" жол болып келеді, ал null None болады — нөл де емес, бос жол да емес.
str None
  1. int(key). JSON-дағы объект кілті — әрқашан жол, сондықтан оны оқығанда қайта санға келтіреді; әйтпесе series[2025] еш жаққа кетпеген жылды таппай қалады.
import json

series = {2025: 11.39, 2024: 8.69}
back = json.loads(json.dumps(series))
fixed = {}
for key, value in back.items():
    fixed[int(key)] = value
print(fixed)
{2025: 11.39, 2024: 8.69}
  1. Кілт еш жаққа кеткен жоқ — ол типін ауыстырды: dumps 2025-ті "2025" деп жазды, әрі loads-тан кейін сөздікте жол тұр. Санмен ол енді табылмайды, сондықтан не жолмен жүгінеді, не — дұрысы — оқығаннан кейін бірден кілттерге санды қайтарады.
import json

series = {2025: 11.39}
back = json.loads(json.dumps(series))
print(back["2025"])
11.39

Дереккөздер

Мәтінде қате не теру қатесі кездессе, бізге айтыңыз

Тапсырманы тексеру

Алдымен VS Code-та шешіп, іске қосыңыз — редактор қатені сол жерде көрсетеді. Дайын шешімді осында қойыңыз. Тексеретін — модель: ол қатені атап көрсетеді, бірақ дайын жауапты бермейді.

Тексеру үшін кіру керек. Кіру

Пікірлер (0)

Әзірге пікір жоқ. Бірінші болыңыз.