Что такое глобальные переменные?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Глобальные переменные
Глобальные переменные — это переменные, определённые на уровне модуля (вне функций и классов), которые доступны из любой части программы. Они существуют в глобальной области видимости и живут столько же, сколько живёт программа.
Как работают глобальные переменные
Определение и использование:
# На уровне модуля — глобальная переменная
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
- Избегай глобального состояния — используй классы и функции с параметрами
- Используй константы вместо переменных — если нужна глобальная область, то константы
- Документируй причину — если используешь глобальную переменную, объясни почему
- Минимизируй глобальные переменные — только конфигурация, логирование и константы
- Тестируй в изоляции — не полагайся на глобальное состояние в тестах
Вывод: Глобальные переменные удобны на первый взгляд, но приводят к проблемам при масштабировании. Профессиональный код использует их минимально, только для констант и конфигурации.