Shanraq.org Shanraq.org
Функциялар: есепке ат және қате санды ұстайтын assert
IT

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

Функциялар: есепке ат және қате санды ұстайтын assert

Python курсының тоғызыншы сабағы. Қатарды аралау ат алады да, кез келген қатарды санайды: Қазақстан — 11,52%, әлем — 4,68%. Қоса бос қатарда есепті тоқтататын `assert`, `sorted(key=)` ішіндегі `lambda` және өлшенген тұзақ: тақырыптағы `into=[]` мәндерді жинап қояды.

Не үшін керек

Өткен сабақта қатарды аралау бағдарламаның ішінде жазылған еді. Сол есепті екінші қатар бойынша жүргізу үшін оны көшіру керек — сол сәттен бастап сізде екі көшірме болады, әрі олар алғашқы түзетуде-ақ айырылады.

Функция есептің бір кесегіне ат береді. Содан кейін ол қанша керек болса, сонша шақырылады, бір жерде тұрады, бір рет түзетіледі.

Бүгін шығатын екінші нәрсе дерек туралы курста синтаксистен маңыздырақ: есептің ішіндегі тексеру. Қате сан айқайламайды — ол есепке дейін тыныш жетеді де, нағызындай көрінеді. assert — оның жолына бөгет қоюдың алғашқы тәсілі.

Бірден тұтас

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

Міндеттісі — екі функция мен олардың шақырылуы: def, return, әдепкі мәні бар аргумент және assert. Одан кейінгінің бәрі — lambda, *args, көріну аймағы — төменде бөлек талданады, тапсырмада керегі жоқ.

"""9-сабақ: функция — есептің бір кесегіне берілген ат.

Өткен сабақта қатарды аралау бағдарламаның ішінде жазылған еді. Сол есепті
екінші қатармен жүргізу үшін оны көшіруге тура келер еді. Мұны ат шешеді.
"""

# Жылдық инфляция, %: Қазақстан және әлем. Сандар бірінші сабақтан, Дүниежүзілік банк.
kz = {2021: 8.0, 2022: 15.0, 2023: 14.5, 2024: 8.7, 2025: 11.4, 2026: None}
world = {2021: 3.5, 2022: 8.1, 2023: 5.8, 2024: 3.0, 2025: 3.0}


def average(series):
    """Қатардың орташасы; саны жоқ жылдар есепке кірмейді."""
    total = 0.0
    count = 0
    for value in series.values():
        if value is None:
            continue
        total += value
        count += 1
    assert count > 0, "қатарда бірде-бір сан жоқ"
    return total / count


def above(series, other, since=2021):
    """Since жылынан бастап бірінші қатар екіншісінен жоғары болған жылдар.

    Екі қатардың бірінде саны жоқ жыл есепке кірмейді.
    """
    years = []
    for year, value in series.items():
        if year < since or value is None or other.get(year) is None:
            continue
        if value > other[year]:
            years.append(year)
    return years


print("== бес жылдың орташасы")
print(f"Қазақстан: {average(kz):.2f}%")
print(f"әлем:      {average(world):.2f}%")

print()
print("== әлемдікінен жоғары жылдар")
print("бәрі:    ", above(kz, world))
print("2023-тен:", above(kz, world, since=2023))

print()
print("== ең жоғары үш жыл")
pairs = []
for year, value in kz.items():
    if value is not None:
        pairs.append((year, value))
top = sorted(pairs, key=lambda pair: pair[1], reverse=True)
print(top[:3])

Шығатыны:

== бес жылдың орташасы
Қазақстан: 11.52%
әлем:      4.68%

== әлемдікінен жоғары жылдар
бәрі:     [2021, 2022, 2023, 2024, 2025]
2023-тен: [2023, 2024, 2025]

== ең жоғары үш жыл
[(2022, 15.0), (2023, 14.5), (2025, 11.4)]

Талдау

def, шегініс және return

def average(series):
    ...
    return total / count

def ат жасайды, жақшалар аргументтерді тізеді, қос нүкте мен шегініс денені береді — if пен for-дағыдай. return мәнді сыртқа шығарады да, функцияның жұмысын сол жерде бітіреді: одан кейінгі жолдар орындалмайды.

return-сіз функция да жұмыс істейді, бірақ None қайтарады — жетінші сабақтағы сол «сан жоқ». Ұмытылған return қате бермейді, есепте бос орын береді, ал оны кейін ұзақ іздейді.

def-тен кейінгі үш тырнақшадағы жол — docstring, функцияның сипаттамасы. Оны файл басында көрдік; мұнда ол бағдарламаны емес, бір кесекті түсіндіреді, әрі функцияның жетіспейтін мәндермен не істейтінін жазатын жер де осы.

Айту аз, істеу керек. above ішінде жетіспейтін мән екі жағынан да тексеріледі: other.get(year) is None «бөтен қатарда жыл жоқ» дегенді де, «жыл бар, саны жоқ» дегенді де жабады. Тек өз қатарымызды тексерсек, value > other[year] бір күні None-ға тап болып, жауаптың орнына TypeError-мен құлар еді. Жетіспейтін мәнтармен жұмыс істеймін деп уәде берген функция өзіне жететін жетіспейтін мәндің бәрін білуге міндетті.

Елестетіп көріңіз. Аты бар рецепт. Ол басыңызда тұрғанда, оны әр жолы қайта айтып бересіз; жазылып, ат алғаннан кейін бір сөзбен беріледі.

Әдепкі мәні бар аргумент және атымен шақыру

def above(series, other, since=2021):

since=2021 — әдепкі мән: онсыз шақырдыңыз, сонымен алдыңыз. above(kz, world, since=2023) аргументті атымен береді, әрі шақыруда 2023 дегеннің не екені көрініп тұр.

Бұл әдетке тұрарлық: above(kz, world, 2023) дәл солай жұмыс істейді, бірақ оқитын адам үшінші аргументтің не болғанын есінде ұстауға тиіс. Шақырудағы ат сол күшті үнемдейді.

Тұзақ: функция тақырыбындағы into=[]

Әдепкі мән Python def-ті оқығанда бір рет есептеледі, әр шақыруда емес. Сан үшін бұл байқалмайды, тізім үшін — апат:

def bad(value, into=[]):
    into.append(value)
    return into

bad(1) → [1]
bad(2) → [1, 2]      ← өткен жолғы сол тізім

Екінші шақыру ішінде бөтен мәні бар тізім алды. Дұрысы былай:

def good(value, into=None):
    if into is None:
        into = []
    into.append(value)
    return into

good(1) → [1]
good(2) → [2]

Ереже қарапайым: функция тақырыбында әдепкі мән ретінде тек өзгермейтін нәрсе тұрады — сан, жол, None. Тізім, сөздік және жиынды ішінде жасайды.

assert: қате пайда болған жерде тоқтау

assert count > 0, "қатарда бірде-бір сан жоқ"

assert сіз сенген нәрсені тексереді, ал ол шындық болмаса — бағдарламаны сіздің мәтініңізбен тоқтатады:

AssertionError: бос қатар

Онсыз average бос қатарда бір қабат төменде нөлге бөлумен құлар еді — дерек туралы емес, бөлу туралы хабармен. assert қатені пайда болған жерінде ұстайды да, адамның тілімен айтады.

Әрі маңызды ескерту. assertесеп туралы өз болжамыңызды тексеру, пайдаланушы жібергенді тексеру емес: python -O арқылы іске қосу оларды түгел өшіреді. Бөтен файлдан келген деректі кәдімгі if-пен әрі түсінікті қатемен тексереді; assert «мұндай болуы мүмкін емес, ал мүмкін болса, мен бірден білгім келеді» деген жерде тұрады.

lambda мен key=: тізім сабағындағы уәде

top = sorted(pairs, key=lambda pair: pair[1], reverse=True)

Тізім сабағында кортеждерді itemgetter арқылы сұрыптап, өз функциямызды жазуға уәде еткен едік. Міне ол: lambda pair: pair[1] — жұпты алып, одан екінші элементті беретін атсыз функция. sorted оны әр элемент үшін шақырады да, қайтарғаны бойынша сұрыптайды.

Дәл сол нәрсені def арқылы да жазуға болады, әрі бір жолдан ұзын нәрсе үшін солай істейді. lambda функция өз атынан қысқа болған жерде ғана орынды.

Қанша керек, сонша аргумент: *args пен **kwargs

Кейде қанша мән келетіні алдын ала белгісіз:

def report(title, *values, **options):
    digits = options.get("digits", 1)
    out = []
    for value in values:
        out.append(f"{value:.{digits}f}")
    return f"{title}: " + ", ".join(out)

report("инфляция", 8.0, 15.0, 14.5)          → инфляция: 8.0, 15.0, 14.5
report("инфляция", 8.0, 15.0, digits=2)      → инфляция: 8.00, 15.00

Бір жұлдызша артық позициялық аргументтерді кортежге жинайды, екеуі — аталғандарын сөздікке. args пен kwargs деген аттар — тек дәстүр; жұмыс істейтіні — жұлдызшалар.

Мұны өз кодыңызда сирек қолданасыз, ал оқитыныңыз жиі: бөтен кітапханалардағы функциялардың жартысы солай құрылған.

Функция ішіндегі ат — өзінікі

count = 0

def bump():
    count = count + 1

bump()
→ UnboundLocalError: cannot access local variable 'count' where it is not associated with a value

Функция ішіндегі меншіктеу жаңа, жергілікті ат жасайды. Python денедегі count = ... дегенді көреді де, count-ты бүкіл функцияда жергілікті деп санайды — оны меншіктеуге дейін оқығысы келген жолды қоса.

global сөзі мұны өшіреді, бірақ дұрыс жауап дерлік әрқашан басқа: мәнді аргументпен қабылдап, нәтижені қайтару. Сырттағы ештеңеге тимейтін функция бір шақырумен тексеріледі — бүгінгі сабақтың мәнінің жартысы да сол.

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

Сабақ картасы: ат, аргумент және тексеру

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

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

  1. Ішінде return жоқ функция не қайтарады?
  2. Функция тақырыбындағы into=[] неге қате, ал into=None неге дұрыс?
  3. assert-тің деректі if-пен тексеруден айырмасы неде?

Жаттығу

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

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

def average(series):
    total = 0.0
    count = 0
    for value in series:
        if value is None:
            continue
        total += value
        count += 1
    return total / count


print(average([8.0, None, 15.0]))

2. Бос орынды толтырыңыз. ... орнына шартты қойыңыз: саны бар және ол шектен үлкен болса, жыл алынады.

def above(series, limit):
    years = []
    for year, value in series.items():
        if ...:
            years.append(year)
    return years


print(above({2024: 8.7, 2025: 11.4, 2026: None}, 10))

3. Жөндеңіз. Бағдарлама алдымен санды, содан кейін None басып шығарады. Себебін түсіндіріп, екінші жол орташаны көрсететіндей етіңіз.

def average(series):
    print(sum(series) / len(series))


result = average([8.0, 15.0])
print("орташа:", result)

Тапсырма

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

kz = {2023: 14.5, 2024: 8.7, 2025: 11.4, 2026: None}
world = {2023: 5.8, 2024: 3.0, 2026: 2.9}

average(series) жасаңыз — қатар бойынша орташа, жетіспейтін мәндер есепке кірмейді, бос қатарды түсінікті мәтіні бар assert тоқтатады. above(series, other) жасаңыз — бірінші қатар екіншісінен жоғары болған жылдар; екі қатардың бірінде саны жоқ жыл есепке кірмейді. Әр қатардың орташасын екі таңбамен және жылдар тізімін басып шығарыңыз.

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

Қазақстан бойынша орташа: 11.53
әлем бойынша орташа: 3.90
әлемдіктен жоғары: [2023, 2024]

Дайын болғаны: шығу жол-жолмен сәйкес келеді; average екі рет жазылмай, екі рет шақырылған; above екі жағында да None тұрған 2026 жылда құламайды, әрі екінші қатарда жоқ 2025-ті алмайды.

Өз деректеріңізде. Өткен сабақтағы өз қатарыңызға average(series) функциясын жасаңыз: жетіспейтін мәндер есепке кірмейді, бос қатарды түсінікті мәтіні бар assert тоқтатады. Екіншісін — above(series, limit) — жасаңыз: ол мәні limit-тен үлкен кілттердің тізімін қайтарсын, әрі оны әртүрлі шекпен екі рет шақырыңыз.

Мұның бәрі step-3 ішінде жиналған — өзіңіз жазып болғаннан кейін салыстырыңыз.

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

  • average-ге digits=2 аргументін қосып, нәтижені дөңгелектеңіз.
  • Өз жұптарыңызды sorted(key=lambda ...) арқылы мәні бойынша да, кілті бойынша да сұрыптаңыз.
  • into=[] бар функция жазып, оны үш рет шақырыңыз да, не жиналғанын қараңыз.

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

Шолу алғашқы аттарын алады: «қатардың орташасы», «нормадан жоғары жылдар». Әрі қарай олар бөлек файлға көшіп, модуль болады, ал басты бағдарламадағы есеп үш жол болып қалады.

Қарыздар. Тексеруіміз әзірге біреу әрі есептің ішінде тұр. Нағыз тексерулер өздері тексеретін кодтан бөлек тұрады; pytest-ке курстың соңында жетеміз, бірақ керекті жердегі assert бүгіннің өзінде жұмыс істейді.

Жауаптар

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

Сұрақтарға

  1. None. return-сіз функция аяғына дейін орындалады да, бос мән қайтарады — қате емес, сондықтан ұмытылған return бірден көрінбейді.
  2. Өйткені әдепкі мән бір рет, def оқылғанда есептеледі. Сонда жасалған тізім шақырулар арасында өмір сүреді де, ішіне бөтен мәндерді жинайды; None өзгермейді, ал жаңа тізім әр шақыруда ішінде жасалады.
  3. assert авторлың есеп туралы болжамын тексереді әрі python -O арқылы іске қосқанда өшеді. Сырттан келген деректі түсінікті қатесі бар кәдімгі if-пен тексереді — оларда assert-ке сүйенуге болмайды.

Жаттығуға

  1. 11.5. Жетіспейтін мән сомаға да, санауышқа да кірмеді, сондықтан 23.0 үшке емес, екіге бөлінеді.
11.5
  1. value is not None and value > limit. Рет маңызды: салыстыруды бірінші қойсаңыз, жетіспейтін мәнде бағдарлама құлайды — None > 10 дегенді салыстыруға болмайды.
def above(series, limit):
    years = []
    for year, value in series.items():
        if value is not None and value > limit:
            years.append(year)
    return years


print(above({2024: 8.7, 2025: 11.4, 2026: None}, 10))
[2025]
  1. Функция басып шығарады, бірақ ештеңе қайтармайды, ал return-сіз функция None қайтарады — оны result алды. Басып шығару мен қайтару — екі бөлек нәрсе: басып шығару адамға көрсетеді, return бағдарламаға береді.
def average(series):
    return sum(series) / len(series)


result = average([8.0, 15.0])
print("орташа:", result)
орташа: 11.5

Дереккөздер

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

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

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

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

Пікірлер (0)

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