Работа с текущим каталогом в Python 3 - полное руководство с примерами
Основные методы получения текущей директории
Наиболее эффективным и рекомендуемым способом получения текущей рабочей директории в Python 3 является использование модуля pathlib, который предоставляет объектно-ориентированный интерфейс для работы с путями. Метод Path.cwd() возвращает объект Path, представляющий текущий каталог. Этот подход предпочтителен благодаря читаемости, кроссплатформенности и встроенной поддержке операций с путями. Альтернативно, можно использовать функцию os.getcwd() из модуля os, которая возвращает строку с абсолютным путем.
from pathlib import Path
import os
# Современный способ
current_dir = Path.cwd()
print(current_dir) # Вывод: /home/user/project (пример)
# Классический способ
current_dir_str = os.getcwd()
print(current_dir_str) # Вывод: /home/user/project
Python 3 directory (текущая директория в python 3)
Оба метода возвращают одну и ту же информацию, но pathlib облегчает дальнейшие манипуляции с путями (например, current_dir / 'subdir').
Как получить директорию, в которой находится исполняемый скрипт?
Этот вариант используется, когда нужно найти файлы относительно расположения самого скрипта, а не рабочей директории. Для этого применяют специальную переменную __file__, которая содержит путь к текущему файлу скрипта.
import os
from pathlib import Path
# Путь к текущему скрипту
script_path = Path(__file__).resolve()
# Директория скрипта
script_dir = script_path.parent
print(script_dir)
# Альтернатива с os
script_dir_os = os.path.dirname(os.path.abspath(__file__))
print(script_dir_os)
Важно: при запуске скрипта через символическую ссылку или из другого места без полного пути, __file__ может быть относительным. Использование resolve() или os.path.abspath преобразует его в абсолютный.
Типичная ошибка: при запуске в интерпретаторе (например, в IDLE или Jupyter) переменная __file__ может отсутствовать, что вызовет NameError. В таких средах следует использовать os.getcwd() или задать путь явно.
Как узнать текущую директорию в многопоточной программе?
Текущая рабочая директория является процессо-ориентированной и общей для всех потоков одного процесса. Изменение в одном потоке повлияет на все остальные. Для изолированного доступа к путям в каждом потоке рекомендуется использовать абсолютные пути или передавать их через аргументы функций.
import threading
import os
def worker():
# Получить текущую директорию (общую для всех потоков)
print(f"Поток {threading.current_thread().name}: {os.getcwd()}")
threads = [threading.Thread(target=worker) for _ in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
Если требуется каждому потоку своя «рабочая» директория, можно хранить её как локальную переменную потока (threading.local) и самостоятельно менять её контекст.
Проблема: при использовании os.chdir() в одном потоке другие потоки внезапно получат изменённую директорию. Решение - избегать глобального изменения рабочей директории в многопоточном коде или использовать контекстные менеджеры для временного переключения.
Как временно сменить рабочую директорию и затем вернуться обратно?
Для временной смены директории часто используют запись текущего пути до изменения и восстановление после выполнения операций. Начиная с Python 3.11, модуль contextlib предоставляет удобный контекстный менеджер chdir.
import os
from contextlib import chdir
# Сохраняем исходную директорию
original_dir = os.getcwd()
# Меняем на /tmp
os.chdir('/tmp')
# ... работа в /tmp
# Возвращаемся
os.chdir(original_dir)
# Или используя contextlib.chdir (Python 3.11+)
with chdir('/tmp'):
# Текущая директория внутри блока - /tmp
print(os.getcwd())
# После выхода из блока - исходная директория
print(os.getcwd())
Для более старых версий Python можно написать свой контекстный менеджер.
Ошибка: если указать несуществующий путь, возникнет FileNotFoundError. Также при недостатке прав - PermissionError. Рекомендуется предварительно проверять существование пути с помощью Path.exists().
Расширенные примеры работы с текущей директорией
Далее приведены более сложные сценарии, демонстрирующие различные аспекты управления текущей директорией в Python 3.
Пример 1: Получение текущей директории и её составных частей с помощью pathlib
from pathlib import Path
cur = Path.cwd()
print("Объект Path:", cur)
print("Родительская директория:", cur.parent)
print("Имя текущей папки:", cur.name)
print("Корень:", cur.anchor)
print("Список компонентов:", cur.parts)
Объект Path: /home/user/docs
Родительская директория: /home/user
Имя текущей папки: docs
Корень: /
Список компонентов: ('/', 'home', 'user', 'docs')
Пример 2: Безопасная смена директории с проверкой существования и обработкой исключений
import os
from pathlib import Path
def safe_chdir(target: str) -> None:
"""Перейти в указанную директорию, если она существует и доступна."""
target_path = Path(target)
if not target_path.exists():
raise FileNotFoundError(f"Директория {target} не существует")
if not target_path.is_dir():
raise NotADirectoryError(f"{target} не является директорией")
try:
os.chdir(target)
except PermissionError:
raise PermissionError(f"Нет прав доступа к {target}")
# Использование
try:
safe_chdir('/var/log')
print("Текущая директория:", os.getcwd())
except (FileNotFoundError, NotADirectoryError, PermissionError) as e:
print("Ошибка:", e)
Текущая директория: /var/log
Пример 3: Использование контекстного менеджера для временного перехода в поддиректорию проекта
import os
from contextlib import contextmanager
@contextmanager
def change_dir(destination):
"""Контекстный менеджер для временной смены директории."""
original = os.getcwd()
try:
os.chdir(destination)
yield
finally:
os.chdir(original)
# Пример использования
with change_dir('/tmp'):
print("Внутри блока:", os.getcwd())
print("Снаружи блока:", os.getcwd())
Внутри блока: /tmp Снаружи блока: /home/user
Пример 4: Получение директории, где находится запущенный скрипт, с учётом возможности запуска из-под символьной ссылки (реальный случай)
import sys
from pathlib import Path
# Если скрипт запущен как __main__, берём его путь
if getattr(sys, 'frozen', False):
# Для упакованных приложений (PyInstaller и др.)
script_dir = Path(sys.executable).parent
else:
script_dir = Path(__file__).resolve().parent
print("Директория скрипта:", script_dir)
# Чтение конфигурационного файла относительно скрипта
config_path = script_dir / 'config.ini'
if config_path.exists():
print("Конфиг найден:", config_path)
else:
print("Конфиг не найден, используем /etc/default/config.ini")
Директория скрипта: /home/user/project/scripts Конфиг найден: /home/user/project/scripts/config.ini
Пример 5: Сравнение os.getcwd() и Path.cwd() при работе с символическими ссылками
import os
from pathlib import Path
# Предположим, что текущая директория /home/user/link, где link -> /var/data
os.chdir('/home/user/link')
print("os.getcwd():", os.getcwd()) # Следует символической ссылке? Зависит от ОС
print("Path.cwd():", Path.cwd()) # Обычно возвращает физический путь после разрешения
# Принудительное получение физического пути
print("Path.cwd().resolve():", Path.cwd().resolve())
os.getcwd(): /home/user/link Path.cwd(): /home/user/link Path.cwd().resolve(): /var/data
В данном примере видно, что resolve() дополнительно раскрывает символические ссылки, что может быть важно для сравнения путей.