← Назад к вопросам

Что такое глобальные переменные?

2.0 Middle🔥 141 комментариев
#Python Core#Soft Skills

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Глобальные переменные

Глобальные переменные — это переменные, определённые на уровне модуля (вне функций и классов), которые доступны из любой части программы. Они существуют в глобальной области видимости и живут столько же, сколько живёт программа.

Как работают глобальные переменные

Определение и использование:

# На уровне модуля — глобальная переменная
DB_CONNECTION = None
MAX_RETRIES = 3
CONFIG = {"host": "localhost", "port": 5432}

def connect_to_db():
    # Внутри функции можно читать глобальную переменную
    return DB_CONNECTION

def setup_connection(url):
    global DB_CONNECTION  # Ключевое слово global для изменения
    DB_CONNECTION = create_connection(url)

Области видимости в Python (LEGB)

Порядок поиска переменных:

# L — Local (локальная область функции)
# E — Enclosing (область объемлющей функции для замыканий)
# G — Global (глобальная область модуля)
# B — Built-in (встроенная область Python)

GLOBAL_VAR = 10

def outer():
    enclosing_var = 20
    
    def inner():
        local_var = 30
        print(local_var)      # L — 30 (локальная)
        print(enclosing_var)  # E — 20 (объемлющая)
        print(GLOBAL_VAR)     # G — 10 (глобальная)
        print(len)            # B — встроенная функция
    
    inner()

outer()

Изменение глобальных переменных

Без global — создаёт локальную переменную:

global_count = 0

def increment():
    global_count = global_count + 1  # ОШИБКА!
    # UnboundLocalError: local variable 'global_count' referenced before assignment
    # Python считает это локальной переменной!

increment()

С global — изменяет глобальную переменную:

global_count = 0

def increment():
    global global_count  # Сигнализируем, что используем глобальную переменную
    global_count += 1

increment()
print(global_count)  # 1
increment()
print(global_count)  # 2

Примеры использования (и проблемы)

Плохо: Глобальные переменные как состояние

# ПЛОХО: Использование глобальных переменных для хранения состояния
logged_in_user = None
authentication_token = None
session_timeout = 3600

def login(username, password):
    global logged_in_user, authentication_token
    if verify_credentials(username, password):
        logged_in_user = username
        authentication_token = generate_token()
        return True
    return False

def get_current_user():
    return logged_in_user

def logout():
    global logged_in_user, authentication_token
    logged_in_user = None
    authentication_token = None

Проблемы:

  • Трудно тестировать (глобальное состояние влияет на все тесты)
  • Трудно отследить изменения
  • Сложно работать с многопроцессностью
  • Конфликты имён

Хорошо: Использование классов и объектов

# ХОРОШО: Инкапсуляция состояния в класс
class AuthenticationManager:
    def __init__(self):
        self.logged_in_user = None
        self.authentication_token = None
        self.session_timeout = 3600
    
    def login(self, username: str, password: str) -> bool:
        if self.verify_credentials(username, password):
            self.logged_in_user = username
            self.authentication_token = self.generate_token()
            return True
        return False
    
    def get_current_user(self) -> str:
        return self.logged_in_user
    
    def logout(self):
        self.logged_in_user = None
        self.authentication_token = None
    
    @staticmethod
    def verify_credentials(username, password):
        return True  # Логика проверки
    
    @staticmethod
    def generate_token():
        return "token_123"

# Использование
auth_manager = AuthenticationManager()
auth_manager.login("john", "password123")
print(auth_manager.get_current_user())  # john

Когда глобальные переменные оправданы

Константы (UPPERCASE):

# ПРАВИЛЬНО: Глобальные константы
MAX_CONNECTIONS = 100
DEFAULT_TIMEOUT = 30
API_VERSION = "v1"
ALLOWED_MIME_TYPES = {"application/json", "text/plain"}

class DatabasePool:
    def __init__(self):
        self.max_size = MAX_CONNECTIONS
        self.timeout = DEFAULT_TIMEOUT

Конфигурация:

import os

# ПРАВИЛЬНО: Глобальная конфигурация
DEBUG = os.getenv("DEBUG", "False") == "True"
DATABASE_URL = os.getenv("DATABASE_URL")
SECRET_KEY = os.getenv("SECRET_KEY")
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")

if DEBUG:
    print("Running in debug mode")

Логирование:

import logging

# ПРАВИЛЬНО: Глобальный логгер
logger = logging.getLogger(__name__)

def process_data():
    logger.info("Processing started")
    # ...

Проблемы с глобальными переменными

1. Сложность тестирования:

# ПЛОХО: Тесты влияют друг на друга
app_state = {"counter": 0}

def increment():
    global app_state
    app_state["counter"] += 1

def test_increment():
    increment()
    assert app_state["counter"] == 1  # Может упасть если тест уже запускался

2. Race conditions при многопроцессности:

# ПЛОХО: Проблема с многопроцессностью
import multiprocessing

counter = 0

def increment():
    global counter
    counter += 1  # Race condition!

if __name__ == "__main__":
    # Каждый процесс имеет свою копию counter
    processes = [multiprocessing.Process(target=increment) for _ in range(10)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    print(counter)  # Вероятно, не 10!

Альтернативы глобальным переменным

Использование параметров функций:

# ХОРОШО
def process_data(config: dict, logger: logging.Logger):
    logger.info(f"Using config: {config}")
    return config["host"]

config = {"host": "localhost"}
logger = logging.getLogger(__name__)
result = process_data(config, logger)

Использование класса:

# ХОРОШО
class DataProcessor:
    def __init__(self, config: dict, logger: logging.Logger):
        self.config = config
        self.logger = logger
    
    def process(self, data):
        self.logger.info(f"Processing with {self.config['host']}")
        return data

processor = DataProcessor(config, logger)
result = processor.process({"data": "value"})

Dependency Injection:

# ХОРОШО: Внедрение зависимостей
from functools import wraps

def with_config(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        config = load_config()
        return func(*args, config=config, **kwargs)
    return wrapper

@with_config
def my_function(data, config=None):
    return config["host"]

Best practices

  • Избегай глобального состояния — используй классы и функции с параметрами
  • Используй константы вместо переменных — если нужна глобальная область, то константы
  • Документируй причину — если используешь глобальную переменную, объясни почему
  • Минимизируй глобальные переменные — только конфигурация, логирование и константы
  • Тестируй в изоляции — не полагайся на глобальное состояние в тестах

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