Стандартная библиотека на каждый день
Python славится «батарейками в комплекте»: в стандартной поставке есть
модули почти на любой случай. Стандартная библиотека Mojo меньше. Она
выросла из задач производительности: в ней отлично проработаны SIMD,
память и параллелизм, а вот JSON и регулярных выражений нет.
В этой главе — то, что нужно в обычной программе: файлы и пути, переменные окружения, время, случайные числа, математика, ввод с клавиатуры и запуск внешних команд. И честный список того, чего нет, с рецептом, как это восполнить.
Где что лежит
Заголовок раздела «Где что лежит»| Python | Mojo | Что внутри |
|---|---|---|
open(), input(), print() | те же, без импорта | файлы, ввод, вывод |
pathlib | std.pathlib | Path |
os, os.path | std.os, std.os.path | каталоги, окружение, операции с путями |
sys | std.sys | argv(), exit, stderr |
time | std.time | замер времени, sleep |
random | std.random | случайные числа |
math | std.math | функции и константы |
tempfile | std.tempfile | временные файлы и каталоги |
subprocess | std.subprocess | только run |
threading, multiprocessing, asyncio | см. главу «Потоки, процессы и async» | |
datetime, json, re, csv, argparse | нет | см. «Чего нет» |
Модули стандартной библиотеки импортируются только с префиксом
std.: from std.math import sqrt. Старые статьи пишут from math import sqrt, и в Mojo 1.x это ошибка:
error: unable to locate module 'math'
До версии 1.0 компилятор находил модули стандартной библиотеки и без
префикса. Теперь имена math, random, collections свободны
для ваших собственных модулей, а стандартные живут в пакете std.
Добавьте префикс: from std.math import sqrt.
Файл открывает встроенная функция open, закрывает блок with — всё как
в Python:
# Запись, дозапись и чтение текстового файла.# Файл создаётся во временном каталоге, который удаляется сам.from std.os.path import joinfrom std.tempfile import TemporaryDirectory
def main() raises: with TemporaryDirectory() as tmp: var path = join(tmp, "notes.txt")
# "w" — создать файл или стереть старое содержимое with open(path, "w") as f: f.write("первая строка\n") f.write("число: ", 42, ", дробь: ", 2.5, "\n")
# "a" — дописать в конец with open(path, "a") as f: f.write("дописали в конец\n")
# "r" — прочитать; read() без аргумента отдаёт весь файл with open(path, "r") as f: var text = f.read() for i, line in enumerate(text.splitlines()): print(i + 1, "|", line)1 | первая строка 2 | число: 42, дробь: 2.5 3 | дописали в конец
Пример пишет во временный каталог: TemporaryDirectory из std.tempfile
создаёт его при входе в with и удаляет со всем содержимым при выходе.
Удобно для примеров и тестов — после программы не остаётся мусора.
Что стоит заметить:
writeпринимает несколько значений любых типов, которые умеют печататься, и пишет их подряд — без пробелов между ними и без перевода строки в конце, в отличие отprint.- Построчно файл не читается. Итерировать
FileHandle, как в Python (for line in f), нельзя, иreadline()у него нет. Читайте целиком и делите методомsplitlines(). - Режимов четыре:
"r"— чтение,"w"— запись с нуля,"a"— дозапись в конец,"rw"— чтение и запись без стирания. Двоичного режима"b"нет: байты читает и пишет пара методовread_bytes()иwrite_bytes(). - Недостающие каталоги создаются сами. В режимах
"w","a"и"rw"Mojo создаёт и файл, и все каталоги на пути к нему. Python в таком случае бросаетFileNotFoundError, так что опечатка в пути здесь не остановит программу, а создаст лишнюю папку.
Ошибки открытия — обычные исключения:
def main(): try: var f = open("нет-такого-файла.txt", "r") print(f.read()) except e: print(e)
try: _ = open("notes.txt", "x") except e: print(e)Failed to open file 'нет-такого-файла.txt': No such file or directory
invalid mode: "x". Can only be one of: {"r", "w", "rw", "a"}Переменная, объявленная внутри with, после блока не видна — эту
ловушку для питонистов мы разбирали в проекте
«Утилита командной строки».
Пути: pathlib
Заголовок раздела «Пути: pathlib»Path из std.pathlib устроен как в Python: пути склеиваются
оператором /, у пути есть имя и расширение, а короткие файлы читаются
и пишутся одним вызовом:
# Пути как объекты: склейка через /, части имени, чтение и запись.from std.os import makedirsfrom std.pathlib import Pathfrom std.tempfile import TemporaryDirectory
def main() raises: var p = Path("data") / "2026" / "report.final.csv" print(p) print(p.name(), p.suffix()) print(p.parts())
with TemporaryDirectory() as tmp: var root = Path(tmp) (root / "a.txt").write_text("альфа") (root / "b.csv").write_text(String("x,y\n", 1, ",", 2, "\n")) makedirs(root / "sub" / "deep")
print((root / "a.txt").read_text()) print((root / "a.txt").exists(), (root / "zzz").exists()) print((root / "a.txt").is_file(), (root / "sub").is_dir())
# listdir() не обещает порядок — сортируем сами var names = List[String]() for child in root.listdir(): names.append(child.name()) sort(names) print(names)data/2026/report.final.csv report.final.csv .csv [data, 2026, report.final.csv] альфа True False True True [a.txt, b.csv, sub]
Отличия от Python:
name(),suffix(),parts()— методы, со скобками, а не свойства.listdir()возвращает только имена —a.txt, а не/tmp/…/a.txt, хоть и в видеPath. Полный путь собирайте сами:root / child. Порядок — тот, в каком их отдала файловая система, то есть произвольный. Нужен порядок — сортируйте.- У
Pathнетmkdir,parent,stem,glob,unlink. Их заменяют функции изstd.osиstd.os.path:makedirs,dirname,split_extension,listdir,remove. Все они принимают иPath, и строку. - Ещё есть
Path.home(),cwd()(текущий каталог, импортируется как функция:from std.pathlib import cwd) иexpanduser()для путей вида~/data.
os и os.path
Заголовок раздела «os и os.path»Функции std.os.path принимают и строку, и Path, а возвращают
строки — удобно, когда путь и так строка. А getenv читает переменные
окружения:
# Функции os.path работают со строками; getenv читает переменные окружения.from std.os import getenv, setenv, unsetenvfrom std.os.path import basename, dirname, join, split_extension
def main() raises: var p = join("data", "2026", "report.csv") print(p) print(basename(p), "|", dirname(p)) var stem_ext = split_extension(p) print(stem_ext[0], "|", stem_ext[1])
# нет переменной — пустая строка или запасное значение print(getenv("MOJO_KURS_NO_SUCH_VAR") == "") print(getenv("MOJO_KURS_NO_SUCH_VAR", "по умолчанию"))
_ = setenv("MOJO_KURS_LEVEL", "3") print(getenv("MOJO_KURS_LEVEL")) _ = unsetenv("MOJO_KURS_LEVEL") print(getenv("MOJO_KURS_LEVEL", "нет"))data/2026/report.csv report.csv | data/2026 data/2026/report | .csv True по умолчанию 3 нет
getenv не бросает исключение и не возвращает Optional: если
переменной нет, вы получите второй аргумент, а без него — пустую строку.
Без второго аргумента «переменная не задана» и «задана пустой»
не различаются. Если разница важна, передайте запасное значение: для
пустой переменной getenv вернёт пустую строку, а не его.
В std.os есть и остальное привычное: listdir, mkdir,
makedirs(path, exist_ok=True), remove, rmdir, chdir. В
std.os.path — exists, isfile, isdir, getsize, expanduser,
expandvars.
Аргументы командной строки, стандартный поток ошибок и код возврата
живут в std.sys:
from std.sys import argv, exit, stderr
def file_from_args() -> String: var args = argv() if len(args) < 2: print("использование: app ФАЙЛ", file=stderr) exit(2) # код 2 — ошибка в аргументах return String(args[1])Всё это подробно, с разбором ключей, — в проекте «Утилита командной строки».
std.time умеет две вещи: мерить промежутки и ждать.
# Паузы и замер времени. Печатаем только то, что не зависит от машины.from std.time import perf_counter, perf_counter_ns, sleep
def main(): var start = perf_counter_ns() sleep(0.05) # секунды, можно дробные var elapsed = perf_counter_ns() - start print("прошло не меньше 50 мс:", elapsed >= 50_000_000)
var t0 = perf_counter() sleep(0.01) var seconds = perf_counter() - t0 print("в секундах, Float64:", seconds >= 0.01)прошло не меньше 50 мс: True в секундах, Float64: True
| Функция | Что возвращает |
|---|---|
perf_counter_ns() | Int, наносекунды — для замеров |
perf_counter() | Float64, секунды |
monotonic() | то же, что perf_counter_ns() |
sleep(sec) | ничего; ждёт sec секунд, можно дробных |
time_function(f) | Int, сколько наносекунд работала функция f |
Все счётчики монотонные: перевод системных часов на них не влияет.
Отсчёт у них начинается с произвольного момента, поэтому
по отдельности их значения ничего не значат, смысл имеет только разность.
А вот текущей даты и времени суток в std.time нет: нет ни
time.time(), ни модуля datetime. Их берут из Python:
from std.python import Python
def main() raises: var datetime = Python.import_module("datetime") var now = datetime.datetime.now() print("сейчас:", now.strftime("%d.%m.%Y %H:%M"))
var today = datetime.date.today() var new_year = datetime.date(today.year + 1, 1, 1) print("до Нового года:", (new_year - today).days, "дн.")сейчас: 30.09.2026 10:55 до Нового года: 93 дн.
Как вызывать Python из Mojo, разобрано в главе
«Вызов Python из Mojo». Для этой задачи
достаточно Python.import_module.
Случайные числа
Заголовок раздела «Случайные числа»from std.random import random_float64, random_si64, random_ui64, seed, shuffle
def main(): seed(2026) var dice = List[Int]() for _ in range(10): dice.append(Int(random_si64(1, 6))) # от 1 до 6 включительно print(dice)
print(random_float64()) # [0, 1) print(random_float64(10, 20)) # [10, 20)
var cards: List[String] = ["туз", "король", "дама", "валет"] shuffle(cards) print(cards) print("выпала:", cards[Int(random_ui64(0, UInt64(len(cards) - 1)))])[4, 5, 1, 3, 4, 4, 2, 6, 3, 3] 0.315924248910995 14.16148431147112 [король, туз, валет, дама] выпала: валет
Генератор написан на самом Mojo (алгоритм Philox), поэтому с одним
и тем же seed он выдаёт одни и те же числа на любой машине. Вы увидите
ровно тот вывод, что выше.
| Python | Mojo |
|---|---|
random.randint(a, b) | Int(random_si64(a, b)) — обе границы включены |
random.random() | random_float64() |
random.uniform(a, b) | random_float64(a, b) |
random.choice(xs) | xs[Int(random_ui64(0, UInt64(len(xs) - 1)))] — список не должен быть пустым |
random.shuffle(xs) | shuffle(xs) |
random.gauss(mu, sigma) | randn_float64(mu, sigma) |
random.seed(n) | seed(n) |
Функция randint в Mojo есть, но делает другое: заполняет случайными
числами готовый буфер. Привычный питоновский вызов не соберётся:
error: no matching function in call to 'randint'
randint(span, low, high) заполняет память, а не возвращает одно
число. Ниже компилятор перечислит варианты с пометкой missing required argument.
Одно число от a до b включительно: Int(random_si64(a, b)).
Математика
Заголовок раздела «Математика»abs, min, max, round и ** встроены, остальное — в std.math:
from std.math import ceil, floor, gcd, isclose, lcm, pi, sqrt
def main(): print(sqrt(2.0), sqrt(16)) print(pi) print(floor(2.7), ceil(2.1), Int(floor(2.7))) print(round(2.5), round(3.5), round(2.567, 2)) print(gcd(12, 18), lcm(4, 6)) var a = 0.1 var b = 0.2 print(a + b == 0.3, isclose(a + b, 0.3))1.4142135623730951 4 3.141592653589793 2.0 3.0 2 2.0 4.0 2.57 6 12 False True
Отличия от Python:
- Тип результата — тип аргумента.
sqrt(16)от целого — целое4, а не4.0.floor(2.7)иround(2.5)отFloat64даютFloat64, а в Python —int. Нужно целое —Int(floor(x)). roundокругляет половину к чётному, как и в Python:2.5→2.0,3.5→4.0.iscloseпо умолчанию терпимее. В Python допуск — 10⁻⁹ относительной погрешности. В Mojo —rtol=1e-05иatol=1e-08: значения как в NumPy. Для точных сравнений задайте допуск явно:isclose(a, b, rtol=1e-12).
В std.math есть и остальное: exp, log, log2, log10,
тригонометрия, hypot, factorial, comb, clamp, ceildiv
(деление с округлением вверх), константы pi, e, tau.
Ввод с клавиатуры
Заголовок раздела «Ввод с клавиатуры»input() работает как в Python: печатает подсказку и возвращает
введённую строку без перевода строки. Разбор числа бросает исключение,
поэтому ввод удобно обернуть в цикл:
def ask_int(prompt: String) raises -> Int: while True: var line = input(prompt) try: return Int(line.strip()) except: print("нужно целое число, попробуйте ещё раз")Сколько вам лет? двадцатьнужно целое число, попробуйте ещё разСколько вам лет? 25Когда ввод кончился — пользователь нажал Ctrl+D или данные пришли
из файла и закончились, — input() бросает ошибку с текстом EOF.
Внешние команды
Заголовок раздела «Внешние команды»run из std.subprocess запускает команду через оболочку
и возвращает её вывод строкой, без завершающих пробелов и переводов строк:
from std.subprocess import run
def main() raises: var out = run("echo привет; echo мир") print(out.replace("\n", " | "))
# код возврата run() не проверяет — спросите его у оболочки сами print("код:", run("grep -q mojo /dev/null; echo $?")) print("исключения нет:", run("exit 3") == "")привет | мир код: 1 исключения нет: True
Это аналог subprocess.check_output(cmd, shell=True) с тремя
отличиями: код возврата не проверяется, результат — String, а не
bytes, и пробелы с переводами строк в конце обрезаны. Поток ошибок
команды, как и в Python, не перехватывается и идёт прямо в терминал.
Команда проходит через оболочку, поэтому не подставляйте в неё
непроверенный ввод пользователя.
Чего нет и что делать
Заголовок раздела «Чего нет и что делать»| Нужно | В Python | В Mojo 1.1 |
|---|---|---|
| дата и время суток | datetime | Python: datetime |
| JSON | json | Python: json |
| регулярные выражения | re | Python: re; для простого — методы строк |
| CSV | csv | для простого — split(","); с кавычками — Python: csv |
| разбор аргументов | argparse | цикл по argv(), как в утилите |
| HTTP, сокеты | urllib, socket | Python |
Кое-что из питоновских collections и соседних модулей зато есть:
Counter и Deque в std.collections, b64encode и b64decode
в std.base64, count, product и cycle в std.itertools:
from std.base64 import b64decode, b64encodefrom std.collections import Counter, Deque
def main() raises: var words = "мяч кот мяч дом мяч кот".split(" ") var counts = Counter[String]() for w in words: counts[String(w)] += 1 for item in counts.most_common(2): print(item[0], item[1])
var queue = Deque[Int]() queue.append(1) queue.append(2) queue.appendleft(0) print(queue.popleft(), queue.pop(), len(queue))
var code = b64encode("Mojo") print(code, len(b64decode(code)))мяч 3 кот 2 0 2 1 TW9qbw== 4
Для остального есть Python: из Mojo он вызывается почти так же, как из самого Python. JSON, например:
from std.python import Python
def main() raises: var json = Python.import_module("json") var data = json.loads('{"name": "Mojo", "version": [1, 1], "fast": true}') var name = String(py=data["name"]) var major = Int(py=data["version"][0]) print(name, major, data["fast"])
var out = Python.dict() out["язык"] = "Mojo" out["глав"] = 42 print(json.dumps(out, ensure_ascii=False))Mojo 1 True
{"язык": "Mojo", "глав": 42}За удобство платят скоростью: каждый вызов Python идёт через
интерпретатор. Для конфигурации, дат и разбора аргументов это
неважно. А в горячем цикле лучше один раз перевести данные
в List и Dict Mojo и дальше работать с ними. Сколько стоит переход
границы, измерено в главе
«Вызов Python из Mojo».
Готовые библиотеки на Mojo, которых нет в стандартной, ищите в сообществе.
🎯 Проверь себя
Почему `from math import sqrt` не компилируется в Mojo 1.1?
С версии 1.0 модули стандартной библиотеки импортируются только
с префиксом std.: from std.math import sqrt. Без префикса
компилятор ищет ваш собственный модуль math и не находит его.
Как прочитать текстовый файл построчно?
Прочитать целиком — f.read() или Path(p).read_text() — и разбить
методом splitlines(). Итерировать FileHandle и вызывать
readline() в Mojo 1.1 нельзя.
Чем опасен `f.read(10)` для файла с русским текстом?
Аргумент — число байтов, а не букв. Если граница придётся на середину
двухбайтовой буквы, read бросит ошибку про неправильный UTF-8.
Программа без `seed` бросает кубик. Что будет при повторных запусках?
Одни и те же числа: генератор стартует с фиксированного состояния.
Чтобы числа менялись от запуска к запуску, вызовите seed() без
аргумента — он возьмёт начальное значение из счётчика времени.
Как получить текущую дату?
В стандартной библиотеке Mojo нет ни datetime, ни часов реального
времени. Берите питоновский модуль:
Python.import_module("datetime").datetime.now().
Почему `log(10.0)` в Mojo и `math.log(10)` в Python дают разные последние цифры?
В std.math функции exp и log считают по быстрым приближённым
формулам, рассчитанным на SIMD. Для Float64 расхождение появляется
в десятом–тринадцатом знаке. Сравнивайте такие числа через isclose.
Что дальше
Заголовок раздела «Что дальше»Последняя глава базового раздела — модули и пакеты: как разложить свой код по файлам и собрать из них библиотеку.
Тексты курса — CC BY-NC-SA 4.0, код примеров — Apache 2.0