Python: деректен өз есебіңізге дейін 17-сабақ (барлығы 56)
Өз модулің мен пакетің: `import`, `__name__` және стандарттыны ауыстырған файл
Python курсының он жетінші сабағы. Шолу бір ұзын файлға дейін өсті, әрі оны атауларға бөлетін кез келді. `import` бөтен файлды тұтас орындайды, `__name__` модульді іске қосылғаннан ажыратады, ал қасындағы өз `csv.py` файлы стандарттысын үнсіз ауыстырады.
Не үшін керек
Шолу бағдарламасы өсті. Бір файлда есептеу де, деректер экспортын оқу да, есепті басып шығару да жатыр — әрі керек атауды табу үшін файлды айналдырады.
Файлдарға бөлу — әдемілік туралы емес. Бұл әр кесектің атымен шақырылатын аты болуы туралы: sana.average(...) бірден оқылады, ал «файлдың ортасындағы бір функция» — жоқ.
Сонымен қатар import көрінгеннен көбірек нәрсе істейтіні белгілі болады: ол бөтен файлды тұтас орындайды, бір рет, әрі нәтижесін есте сақтайды.
Бірден тұтас
modul.py файлы. Іске қосу: ортадан python modul.py.
Әдетте модульдерді адам жазады — бір рет, қолмен. Мұнда оларды бағдарламаның өзі жазады: сонда сабақ бір командамен іске қосылады да, кодты ғана емес, одан не шыққанын да көреміз. Міндеттісі — алғашқы екі блок: модульді импорттау және пакеттен импорттау.
"""17-сабақ: модуль — атымен шақырылатын файл.
Шолу өсті: есептеу, файлды оқу, есепті басып шығару бір файлда жатыр, әрі ондағы
атауды айналдырып іздейді. Файлдарға бөлетін кез — әрі `import` кезінде шын
мәнінде не болатынын білетін кез.
"""
import importlib
import shutil
import sys
from pathlib import Path
HERE = Path(__file__).parent
# Әдетте бұл файлдарды адам жазады. Мұнда оларды бағдарлама жазады: сонда сабақты
# бір командамен іске қосып, не шыққанын көруге болады.
(HERE / "sana.py").write_text('''"""Шолу есептеулері: бір іс — бір орын."""
ROUND_TO = 2
def average(values):
"""Қатар бойынша орташа; олқылықтар есепке кірмейді."""
numbers = [value for value in values if value is not None]
return round(sum(numbers) / len(numbers), ROUND_TO)
print(" (sana.py файлы импорт кезінде орындалады, __name__ =", __name__, end=")\\n")
if __name__ == "__main__":
print(" бұл кесек тек тікелей іске қосқанда жұмыс істейді")
''', encoding="utf-8")
package = HERE / "sholu"
package.mkdir(exist_ok=True)
(package / "__init__.py").write_text('"""Шолу пакеті: импорттауға болатын қалта."""\n', encoding="utf-8")
(package / "esep.py").write_text('''"""Есепті басып шығару: адам көретін нәрсе."""
def line(year, value):
"""Есептің бір жолы."""
return f"{year}: {value:.1f}%"
''', encoding="utf-8")
importlib.invalidate_caches()
print("== импорт: файл атқа айналады")
import sana
print("модуль аты:", sana.__name__)
print("орташа: ", sana.average([8.0, None, 15.0]))
print("тұрақты: ", sana.ROUND_TO)
from sana import average
print("сол from ... import арқылы:", average([1.0, 2.0]))
print()
print("== пакет: __init__.py бар қалта")
from sholu import esep
print("пакеттегі модуль аты:", esep.__name__)
print("есеп жолы:", esep.line(2025, 11.4))
print()
print("== іске қосылған файлдың __name__-і")
print("мұнда __name__ =", __name__)
print()
print("== екі тұзақ")
try:
import sanaa
except ModuleNotFoundError as error:
print("атаудағы қате:", error)
(HERE / "csv.py").write_text("MINE = True\n", encoding="utf-8")
importlib.invalidate_caches()
import csv
print("қасында өз csv.py:", getattr(csv, "MINE", False), "| csv.reader бар ма:", hasattr(csv, "reader"))
print("import алдымен осында қарайды:", Path(sys.path[0]).name == HERE.name)
# Өзімізден кейін жинаймыз: файлдар, пакет және байт-код кэші.
for name in ("sana.py", "csv.py"):
(HERE / name).unlink()
shutil.rmtree(package)
shutil.rmtree(HERE / "__pycache__", ignore_errors=True)
print("жиналды, қасындағы файл:", len(list(HERE.glob("*.py"))) - 1)
Шығатыны:
== импорт: файл атқа айналады
(sana.py файлы импорт кезінде орындалады, __name__ = sana)
модуль аты: sana
орташа: 11.5
тұрақты: 2
сол from ... import арқылы: 1.5
== пакет: __init__.py бар қалта
пакеттегі модуль аты: sholu.esep
есеп жолы: 2025: 11.4%
== іске қосылған файлдың __name__-і
мұнда __name__ = __main__
== екі тұзақ
атаудағы қате: No module named 'sanaa'
қасында өз csv.py: True | csv.reader бар ма: False
import алдымен осында қарайды: True
жиналды, қасындағы файл: 0
Талдау
import — «қосу» емес, файлды орындау
(sana.py файлы импорт кезінде орындалады, __name__ = sana)
Бұл жол бірде-бір функцияны шақырмай тұрып басылды. Өйткені import sana дегені — «sana.py файлын тап, оны тұтас орында да, шыққан атауларды модуль объектісіне сал».
Осыдан тәжірибелік ереже шығады: модульдің жоғарғы деңгейінде әрекет емес, анықтама ұсталады. Функциялар, кластар, тұрақтылар — иә. Желіге сұраныс, файл оқу, басып шығару — жоқ: бұның бәрі жай ғана бір функция алғысы келген адамда импорт кезінде болып өтеді.
Файл бір рет орындалады. Бағдарламаның басқа жеріндегі екінші import sana ештеңе орындамайды — Python дайын модульді sys.modules-тан береді.
Елестетіп көріңіз. Аспап тұрған сөре. Сөрені бөлмеге кіргізу — одан балға алумен бірдей емес. Сөре кіргізген кезде өзі шеге қаға бастаса, жаман болар еді.
__name__: мен кіммін — модуль бе, бағдарлама ба
модуль аты: sana
мұнда __name__ = __main__
Әр файлда __name__ айнымалысы бар. Файлды импорттаса, онда оның аты тұрады — sana. Файлды іске қосса — онда __main__.
Осыдан бөтен кодта бәрінен жиі кездесетін сол жол шығады:
if __name__ == "__main__":
main()
Ол «мұны тек файлды тікелей іске қосқанда орында» дегенді білдіреді. Сонда бір файл әрі функция алатын модуль, әрі іске қосылатын бағдарлама бола алады. Онсыз модульді импорттау оның бүкіл жұмысын жүргізіп жіберер еді.
Импорттың екі түрі
# модульді тұтас аламыз: sana.average(...) деп жүгінеміз
import sana
# бір атауды аламыз: average(...) деп жүгінеміз
from sana import average
Бірінші түрі дереккөзді әр шақыруда көрсетіп тұрады: sana.average — функцияның қайдан екені бірден түсінікті. Екіншісі қысқа, бірақ бөтен файлда average(...) оның кімдікі екенін айтпайды.
Ереже қарапайым: атау көп немесе олар жалпыға ортақ болса, модульді тұтас алады (average, line, load бәрінде бар), ал from ... import — атау сирек әрі атынан көрініп тұрғанда. from sana import * жұлдызшасын ешқашан алмайды: ол файлға сіз атамаған нәрсенің бәрін кіргізеді.
Пакет — импорттауға болатын қалта
пакеттегі модуль аты: sholu.esep
Пакет — ішінде модуль-файлдары және __init__.py файлы бар қалта. Ол бос болуы мүмкін: оның рөлі — «бұл қалта жай қалта емес, пакет» деу, әрі пакеттің сыртқа не көрсететінін шешетін орын болу.
Нүкте арқылы жүгінеді: from sholu import esep, содан кейін esep.line(...). Пакет ішіндегі модульдің аты толық: sholu.esep.
Жобада осылай құрылым пайда болады: sholu/esep.py — басып шығару, sholu/derek.py — оқу, sana.py — есептеу. Әр атау оны іздейтін жерде жатады.
Екі тұзақ
атаудағы қате: No module named 'sanaa'
ModuleNotFoundError — жаңадан бастаған адамның ең жиі қатесі, әрі оның үш себебі бар: қате теру, файл бағдарламаның қасында жатпайды, немесе сіз басқа ортадасыз. Тексеруді осы ретпен жүргізеді — екінші сабақ дәл сол туралы еді.
қасында өз csv.py: True | csv.reader бар ма: False
Ал бұл — қымбат тұзақ. Python модульді алдымен іске қосылған бағдарламаның қасынан іздейді, тек содан кейін стандартты кітапханадан. Өз файлыңызды csv.py, json.py, random.py деп атаңыз — да, import csv кітапхананы емес, сіздің файлыңызды табады.
Қате қисынсыз көрінеді: «csv модулінде reader жоқ», ал ол кеше сонда тұрған. Жаңа Python қатенің мәтінінде тіке айтады: consider renaming '.../json.py' since it has the same name as the standard library module. Емі біреу — өз файлыңыздың атын өзгерту.
Сабақ картасы
Өз сөзіңізбен айтыңыз
Қарамай, дауыстап немесе қағазға жауап беріңіз. Жауаптары — сабақтың соңында.
import sanaсәтінде не болады, әрі модульдің жоғарғы деңгейінде неге әрекет жазбайды?- Импортталған және іске қосылған файлда
__name__неге тең? - Бағдарламаның қасындағы өз
csv.pyфайлыimport csv-ті неге бұзады?
Жаттығу
Тапсырмаға дейінгі үш қысқа қадам: болжау, толықтыру, жөндеу. Жауаптары — сабақтың соңында, бірақ алдымен өзіңіз жауап беріңіз.
1. Болжаңыз. Файл тікелей іске қосылды. Ол не басып шығарады?
def main():
print("main жұмыс істеді")
if __name__ == "__main__":
main()
2. Бос орынды толтырыңыз. ... орнына pathlib-тен бір керек атауды алыңыз.
# импортты бір керек атаумен толықтырыңыз
from pathlib import ...
print(Path("dannye.csv").suffix)
3. Жөндеңіз. Модуль стандартты әрі еш жаққа кетпесе де, бағдарлама json.dumps-та құлайды. Қатені оқып, атын өзгертумен жөндеңіз.
# бағдарлама өз қасына json.py файлын қояды — өз файлын стандартты модульдің
# атымен атаған адам да дәл солай істейді
from pathlib import Path
HERE = Path(__file__).parent
(HERE / "json.py").write_text("MINE = True\n", encoding="utf-8")
import json
print(json.dumps({"year": 2025}))
Тапсырма
Міндетті. Есептеуді файлдарға бөліңіз. Берілгені:
series = {2023: 14.5, 2024: 8.7, 2025: 11.4, 2026: None}
Екі функциясы бар sana.py модулін жасаңыз: average(values) — жетіспейтін мәнсіз орташа — және above(series, limit) — мәні шектен үлкен жылдардың тізімі. __init__.py мен esep.py бар sholu/ пакетін жасаңыз, ал онда title(text) (бас әріппен тақырып) және line(year, value) (2025: 11.4% түріндегі жол) болсын. Басты бағдарламада екеуін де импорттап, есепті басып шығарыңыз, ал соңғы жолмен — модульдің атын және бағдарламаның өз атын.
Күтілетін шығу:
ИНФЛЯЦИЯ
2023: 14.5%
2024: 8.7%
2025: 11.4%
2026: дерек жоқ
орташа: 11.53
оннан жоғары: [2023, 2025]
модуль: sana | бұл бағдарлама: __main__
Дайын болғаны: шығу жол-жолмен сәйкес келеді; sana.py мен esep.py-дың жоғарғы деңгейінде бірде-бір print жоқ; есепті модульдер емес, басты бағдарлама басып шығарады; саны жоқ жыл орташаға да, тізімге де кірмеген.
Өз деректеріңізде. Өткен сабақтардағы өз бағдарламаңызды алып, оны екі файлға бөліңіз: есептеу бөлек, басып шығару бөлек. Іске қосыңыз да, есептеуді импорттағанда ештеңе өздігінен басылмайтынына көз жеткізіңіз.
Мұның бәрі step-6 ішінде жиналған — өзіңіз жазып болғаннан кейін салыстырыңыз.
Қалауыңызша.
sana.py-ға шағын тексеруі барif __name__ == "__main__":жолын қосып, файлды тікелей іске қосыңыз.sana.__file__дегенді басып шығарып, Python оны қайдан алғанын қараңыз.- Бағдарламаның қасына
random.pyфайлын қойып,import randomжасап көріңіз.
Жобада бұл қайда тұрады
Шолу бір файл болуын қояды. Есептеу, деректер экспортын оқу және басып шығару өз орындарына тарайды, әрі әр атау енді мекенжайымен шақырылады. Бұл — бөліктерді бөлек тексеруге болатын жағдайға апаратын алғашқы қадам: тесттерге аз қалды.
Қарыздар. Біз sys.path-ты да, өз пакетіңді pip install -e . арқылы орнатуды да қозғаған жоқпыз: әзірге модульдердің бағдарламаның қасында жатқаны жеткілікті. Ал __init__.py-ымыз бос — пакеттің сыртқа көрсететін нәрсесі болғанда, бұл шындық болуын қояды.
Жауаптар
Жауаптарды көрсету
Сұрақтарға
- Python файлды табады, оны тұтас орындайды да, шыққан атауларды модуль объектісіне салады; қайта импорттау дайынын
sys.modules-тан алады. Сондықтан жоғарғы деңгейдегі әрекет жай ғана бір функция алғысы келген адамда орындалады. - Импортталғанда — файлдың аты (
sana), іске қосылғанда —__main__.if __name__ == "__main__":дегені де соған негізделген. - Өйткені импорт кезінде Python алдымен іске қосылған бағдарламаның қасынан, тек содан кейін стандартты кітапханадан қарайды. Сіздің файлыңыз бірінші табылады, ал онда, әрине,
readerде,dumpsта жоқ.
Жаттығуға
main жұмыс істеді. Файл тікелей іске қосылды, демек__name____main__-ге тең, шарт дұрыс, әріmain()шақырылады. Дәл сол файлды импорттаса, ештеңе басылмас еді.
main жұмыс істеді
Path. Модульден кейін пайдаланылатын атауды ғана алады;from pathlib import *қалғанының бәрін де кіргізер еді.
from pathlib import Path
print(Path("dannye.csv").suffix)
.csv
- Файл
json.pyдеп аталған әрі бағдарламаның қасында жатыр, сондықтанimport jsonстандартты модульді емес, соны табады:AttributeError: module 'json' has no attribute 'dumps' (consider renaming ...). Өз файлының атын өзгертеді:
from pathlib import Path
HERE = Path(__file__).parent
(HERE / "moi_json.py").write_text("MINE = True\n", encoding="utf-8")
import json
print(json.dumps({"year": 2025}))
(HERE / "moi_json.py").unlink()
{"year": 2025}
Пікірлер (0)
Пікір қалдыру үшін кіріңіз →
Әзірге пікір жоқ. Бірінші болыңыз.