Если вы до сих пор пишете os.path.join(os.path.dirname(__file__), 'data', 'file.txt'), пришло время остановиться. В Python уже почти 10 лет (с версии 3.4) существует модуль pathlib, который делает работу с файловой системой интуитивно понятной, безопасной и кроссплатформенной.
Давайте разберем, почему объектно-ориентированный подход побеждает, и посмотрим на цифры.
Синтаксис: Глаза не болят
Главное преимущество — наглядность. Вместо вложенных функций мы используем оператор /. Это не просто красиво, это читается как настоящий путь в файловой системе.
Старый стиль (os.path):
import os
config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config', 'settings.json')Новый стиль (pathlib):
from pathlib import Path
config_path = Path(__file__).parent / 'config' / 'settings.json'Согласитесь, второй вариант сразу говорит сам за себя.
Джекпот функций «из коробки»
Path — это не просто строка. Это объект, который содержит десятки методов для любых сценариев:
- Навигация:
Path.cwd()— текущая директория,Path.home()— домашняя папка. - Проверки:
path.exists(),path.is_file(),path.is_dir(). - Операции:
path.resolve()— получить абсолютный путь,path.with_suffix('.txt')— сменить расширение,path.with_name('new.csv')— сменить имя файла. - Поиск:
path.glob('*.py')— поиск по шаблону,path.rglob('*')— рекурсивный поиск всех файлов.
Чтение и запись: Элегантность в одну строку
Забудьте про with open(...) as f, если вам нужно просто прочитать небольшой файл целиком.
# Чтение конфига
data = Path('config.json').read_text(encoding='utf-8')
# Запись данных
Path('output.txt').write_text('Hello, world!', encoding='utf-8')Это сокращает код на 3-4 строки и делает его чище.
Замеры скорости (Бенчмарки)
Многие разработчики опасаются, что pathlib медленнее из-за того, что он создает объекты, а не просто оперирует строками. Я прогнал простые тесты (100 000 итераций), чтобы развеять мифы.
Тест 1: Склейка путей (join vs /)
| Операция | os.path | pathlib | Разница |
|---|---|---|---|
| Скорость | ~0.11 сек. | ~0.28 сек. | pathlib ~ в 2.5 раза медленнее |
Вывод: os.path быстрее на чистой склейке строк. Но если вы не склеиваете пути в цикле из миллиона элементов, эта разница в 0.17 секунды абсолютно незаметна для пользователя.
Тест 2: Проверка существования файла (exists)
| Операция | os.path.exists | pathlib.exists | Разница |
|---|---|---|---|
| Скорость | ~0.09 сек. | ~0.10 сек. | Практически идентично |
Вывод: Как только дело доходит до обращения к диску (I/O), накладные расходы на создание объекта Path нивелируются. В реальных проектах узким местом всегда будет жесткий диск, а не Python.
Тест 3: Чтение файла (open vs read_text)
| Операция | Стандартный open | Path.read_text | Разница |
|---|---|---|---|
| Скорость | ~0.62 сек. | ~0.64 сек. | Разница в пределах погрешности |
Вывод: Внутри read_text используется тот же самый open(). Потеря скорости составляет доли процента, а код становится короче.
Главный аргумент: Кроссплатформенность
Это убийственная фича pathlib. В os.path вы используете жесткие слеши (/) или обратные слеши (\)? В Windows это \, в Linux и macOS — /.
Плохо (будет работать только на Linux/macOS или только на Windows):
path = 'data\\folder\\file.txt' # Ошибка на Linux
path = 'data/folder/file.txt' # Ошибка на Windowspathlib автоматически подставляет правильный разделитель для вашей операционной системы.
Отлично (работает везде):
path = Path('data') / 'folder' / 'file.txt'Даже если вы передаете строку с обратными слешами в Windows, pathlib корректно их обработает:
p = Path('C:\\Users\\admin\\docs')
print(p.parts) # Вывод: ('C:\\', 'Users', 'admin', 'docs')Итог
pathlib — это стандарт де-факто современного Python. Да, он чуть медленнее на микро-операциях со строками (что логично, ведь он создает объекты), но это цена за читаемость и безопасность кода.
Переходите на pathlib уже сегодня:
- Ваш код станет короче на 20–30%.
- Вы забудете о проблемах с разделителями путей.
- Вы получите доступ к методам вроде
.glob()и.read_text().
Совет: Не мучайтесь с os.path в новом коде. Используйте from pathlib import Path — это Python-путь джедая.
Код бенчмарков
import os
import time
from pathlib import Path
# Подготовка: создаем тестовый файл
test_file = Path('test_temp.txt')
test_file.write_text('Hello, world!', encoding='utf-8')
# Количество итераций
N = 100_000
print(f"Запуск тестов с {N:,} итерациями...\n")
# ============================================
# ТЕСТ 1: Склейка путей
# ============================================
def test_os_path_join():
base = os.path.dirname(os.path.abspath(__file__))
for _ in range(N):
path = os.path.join(base, 'data', 'folder', 'file.txt')
def test_pathlib_slash():
base = Path(__file__).parent
for _ in range(N):
path = base / 'data' / 'folder' / 'file.txt'
# Замеряем os.path.join
start = time.perf_counter()
test_os_path_join()
os_time = time.perf_counter() - start
# Замеряем pathlib
start = time.perf_counter()
test_pathlib_slash()
plib_time = time.perf_counter() - start
print("📁 ТЕСТ 1: Склейка путей")
print(f" os.path.join : {os_time:.4f} сек.")
print(f" pathlib '/' : {plib_time:.4f} сек.")
print(f" Разница : {plib_time/os_time:.2f}x")
print()
# ============================================
# ТЕСТ 2: Проверка существования файла
# ============================================
def test_os_exists():
for _ in range(N):
os.path.exists('test_temp.txt')
def test_pathlib_exists():
for _ in range(N):
Path('test_temp.txt').exists()
# Замеряем os.path.exists
start = time.perf_counter()
test_os_exists()
os_time = time.perf_counter() - start
# Замеряем pathlib.exists
start = time.perf_counter()
test_pathlib_exists()
plib_time = time.perf_counter() - start
print("🔍 ТЕСТ 2: Проверка существования файла")
print(f" os.path.exists : {os_time:.4f} сек.")
print(f" pathlib.exists : {plib_time:.4f} сек.")
print(f" Разница : {plib_time/os_time:.2f}x")
print()
# ============================================
# ТЕСТ 3: Чтение файла
# ============================================
def test_os_open_read():
for _ in range(N):
with open('test_temp.txt', 'r', encoding='utf-8') as f:
content = f.read()
def test_pathlib_read_text():
for _ in range(N):
content = Path('test_temp.txt').read_text(encoding='utf-8')
# Замеряем стандартный open
start = time.perf_counter()
test_os_open_read()
os_time = time.perf_counter() - start
# Замеряем pathlib.read_text
start = time.perf_counter()
test_pathlib_read_text()
plib_time = time.perf_counter() - start
print("📖 ТЕСТ 3: Чтение файла")
print(f" open() + read : {os_time:.4f} сек.")
print(f" Path.read_text : {plib_time:.4f} сек.")
print(f" Разница : {plib_time/os_time:.2f}x")
print()
# ============================================
# Очистка
# ============================================
test_file.unlink()
print("✅ Тесты завершены. Временный файл удален.")
Файл с тестами можно скачать с Boosty.to
Как можно отблагодарить:
- Оформить удобную для вас подписку на Boosty.to
- Разово поддержать через DonationAlerts
