Перейти к содержимому

Стандартная библиотека на каждый день

Python славится «батарейками в комплекте»: в стандартной поставке есть модули почти на любой случай. Стандартная библиотека Mojo меньше. Она выросла из задач производительности: в ней отлично проработаны SIMD, память и параллелизм, а вот JSON и регулярных выражений нет.

В этой главе — то, что нужно в обычной программе: файлы и пути, переменные окружения, время, случайные числа, математика, ввод с клавиатуры и запуск внешних команд. И честный список того, чего нет, с рецептом, как это восполнить.

PythonMojoЧто внутри
open(), input(), print()те же, без импортафайлы, ввод, вывод
pathlibstd.pathlibPath
os, os.pathstd.os, std.os.pathкаталоги, окружение, операции с путями
sysstd.sysargv(), exit, stderr
timestd.timeзамер времени, sleep
randomstd.randomслучайные числа
mathstd.mathфункции и константы
tempfilestd.tempfileвременные файлы и каталоги
subprocessstd.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:

notes.mojo
# Запись, дозапись и чтение текстового файла.
# Файл создаётся во временном каталоге, который удаляется сам.
from std.os.path import join
from 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, после блока не видна — эту ловушку для питонистов мы разбирали в проекте «Утилита командной строки».

Path из std.pathlib устроен как в Python: пути склеиваются оператором /, у пути есть имя и расширение, а короткие файлы читаются и пишутся одним вызовом:

paths.mojo
# Пути как объекты: склейка через /, части имени, чтение и запись.
from std.os import makedirs
from std.pathlib import Path
from 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.

Функции std.os.path принимают и строку, и Path, а возвращают строки — удобно, когда путь и так строка. А getenv читает переменные окружения:

env.mojo
# Функции os.path работают со строками; getenv читает переменные окружения.
from std.os import getenv, setenv, unsetenv
from 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 умеет две вещи: мерить промежутки и ждать.

timing.mojo
# Паузы и замер времени. Печатаем только то, что не зависит от машины.
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 он выдаёт одни и те же числа на любой машине. Вы увидите ровно тот вывод, что выше.

PythonMojo
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
дата и время сутокdatetimePython: datetime
JSONjsonPython: json
регулярные выраженияrePython: re; для простого — методы строк
CSVcsvдля простого — split(","); с кавычками — Python: csv
разбор аргументовargparseцикл по argv(), как в утилите
HTTP, сокетыurllib, socketPython

Кое-что из питоновских collections и соседних модулей зато есть: Counter и Deque в std.collections, b64encode и b64decode в std.base64, count, product и cycle в std.itertools:

from std.base64 import b64decode, b64encode
from 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.

Последняя глава базового раздела — модули и пакеты: как разложить свой код по файлам и собрать из них библиотеку.

Примеры проверены на Mojo 1.1.0

Тексты курса — CC BY-NC-SA 4.0, код примеров — Apache 2.0