Переменные окружения помогают отделить конфигурацию от кода: вы не хардкодите пароли и ключи API, а читаете их из окружения. В Python для этого есть словарь os.environ , а для удобной локальной разработки — библиотека python-dotenv , читающая значения из файла .env .

Переменные окружения в Python: os.environ и dotenv (практическое руководство)

Переменные окружения помогают отделить конфигурацию от кода: вы не хардкодите пароли и ключи API, а читаете их из окружения. В Python для этого есть словарь os.environ, а для удобной локальной разработки — библиотека python-dotenv, читающая значения из файла .env.

Что такое переменные окружения и зачем они нужны

  • Хранение секретов: токены, пароли, ключи API.
  • Универсальная конфигурация: один и тот же код работает в dev, staging и prod средах без правок исходников.
  • Совместимость: работает в Docker, CI/CD и на серверах без дополнительных зависимостей.

os.environ: чтение, запись, удаление

os.environ — это отображение (похоже на словарь), которое содержит переменные окружения текущего процесса.

import os

# Чтение: безопасно с умолчанием
home = os.environ.get("HOME", "")
print("HOME:", home)

# Жёсткое чтение (поднимет KeyError, если нет переменной)
# path = os.environ["PATH"]

# Установка переменной для текущего процесса (и дочерних)
os.environ["API_TOKEN"] = "secret-123"
print("API_TOKEN set:", os.environ.get("API_TOKEN"))

# Удаление переменной (если отсутствует — KeyError)
if "API_TOKEN" in os.environ:
    del os.environ["API_TOKEN"]

os.putenv vs os.environ

os.putenv существует для совместимости с низкоуровневым API ОС, но в большинстве случаев достаточно работы с os.environ. Изменения в os.environ автоматически отражаются в окружении дочерних процессов.

Безопасные паттерны чтения конфигурации

Заведите небольшие утилиты для обязательных и необязательных переменных с преобразованием типов.

import os

class ConfigError(RuntimeError):
    pass

def get_required(name: str) -> str:
    value = os.environ.get(name)
    if value is None or value == "":
        raise ConfigError(f"Отсутствует обязательная переменная окружения: {name}")
    return value

def get_int(name: str, default: int) -> int:
    raw = os.environ.get(name)
    if raw is None:
        return default
    try:
        return int(raw)
    except ValueError:
        raise ConfigError(f"Переменная {name} должна быть целым числом (получено: {raw!r})")

def get_bool(name: str, default: bool) -> bool:
    raw = os.environ.get(name)
    if raw is None:
        return default
    return raw.strip().lower() in {"1", "true", "yes", "y", "on"}

.env и библиотека python-dotenv

Для локальной разработки удобно хранить значения в файле .env и подгружать их при старте приложения.

# Установка
pip install python-dotenv

Пример файла .env (не коммитьте его в репозиторий):

# .env
API_TOKEN=secret-123
DB_URL=postgresql+psycopg://user:pass@localhost:5432/app
DEBUG=true
PORT=8080
# Комментарии начинаются с #

Загрузка значений в окружение:

import os
from dotenv import load_dotenv, find_dotenv

# Ищет .env, поднимаясь по дереву каталогов, и загружает его
load_dotenv(find_dotenv())

print("API_TOKEN:", os.getenv("API_TOKEN"))

По умолчанию load_dotenv не перезаписывает уже существующие переменные. Для явного перекрытия используйте override=True.

load_dotenv(find_dotenv(), override=True)

Несколько файлов .env

  • .env — базовый файл для разработки.
  • .env.local — локальные переопределения (в .gitignore).
  • .env.production — значения для продакшена (храните вне репозитория).

Вы можете явно загрузить нужный файл:

from dotenv import load_dotenv
load_dotenv(".env.production")

Мини‑проект: конфигурация через окружение

Соберём простой модуль конфигурации, который валидирует обязательные значения и приводит типы.

# config.py
import os
from dotenv import load_dotenv, find_dotenv

load_dotenv(find_dotenv())  # безопасно для локалки

class ConfigError(RuntimeError):
    pass

def _required(name: str) -> str:
    value = os.environ.get(name)
    if not value:
        raise ConfigError(f"Отсутствует {name}. Задайте её в окружении или .env")
    return value

def _to_bool(value: str, default: bool=False) -> bool:
    if value is None:
        return default
    return value.strip().lower() in {"1", "true", "yes", "y", "on"}

def _to_int(value: str, default: int) -> int:
    if value is None:
        return default
    try:
        return int(value)
    except ValueError as e:
        raise ConfigError(f"Ожидалось целое число, получено: {value!r}") from e

API_TOKEN = _required("API_TOKEN")
DB_URL    = _required("DB_URL")
DEBUG     = _to_bool(os.environ.get("DEBUG"), default=False)
PORT      = _to_int(os.environ.get("PORT"), default=8000)
# main.py
from config import API_TOKEN, DB_URL, DEBUG, PORT

def run():
    print("Config loaded:\n",
          f"API_TOKEN=**** (len={len(API_TOKEN)})\n",
          f"DB_URL={DB_URL}\n",
          f"DEBUG={DEBUG}\n",
          f"PORT={PORT}")

if __name__ == "__main__":
    run()

Запуск с передачей переменных на лету:

# Linux/macOS (bash/zsh)
API_TOKEN=xxx DB_URL=sqlite:///db.sqlite3 python main.py

# Windows PowerShell
$env:API_TOKEN="xxx"; $env:DB_URL="sqlite:///db.sqlite3"; python .\main.py

# Windows CMD
set API_TOKEN=xxx && set DB_URL=sqlite:///db.sqlite3 && python main.py

Советы и частые ошибки

  • Не коммитьте .env с секретами. Добавьте .env в .gitignore и держите образец .env.example без реальных значений.
  • Всегда валидируйте обязательные переменные и явно приводите типы (int, bool).
  • Не используйте os.environ["KEY"] без проверки — ловите KeyError. Предпочтительнее os.getenv или обёртки.
  • Для продакшена устанавливайте переменные окружения на уровне оркестратора (Docker, systemd, CI/CD), а не из .env.
  • Не логируйте секреты. Если нужно отладить — логируйте только факт наличия и длину.

FAQ коротко

Как задать переменную только для одного запуска?

# Linux/macOS
DEBUG=1 python app.py

Как проверить, установлена ли переменная?

import os
if os.getenv("API_TOKEN"):
    print("Токен задан")
else:
    print("Токен отсутствует")

Почему моих переменных из .env не видно? Убедитесь, что вызвали load_dotenv до чтения переменных и что путь к .env корректный (используйте find_dotenv()).

Куда двигаться дальше

Итоги

Переменные окружения — фундаментальный инструмент конфигурации в Python. Используйте os.environ и python-dotenv для удобной разработки, валидируйте обязательные параметры, храните секреты вне репозитория и не забывайте про безопасность в логах. Такой подход упрощает переносимость, снижает риски и помогает быстро масштабировать приложение между средами.