Работа с текущим каталогом в 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() дополнительно раскрывает символические ссылки, что может быть важно для сравнения путей.

Текущая директория в Python 3 - comments

En
Python 3 directory (python)