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

В чем разница между объявлением переменной в инициализаторе и переменной класса?

1.0 Junior🔥 191 комментариев
#Python Core

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

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

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

Разница между переменными в инициализаторе и переменными класса

Это частая ошибка новичков в Python, которая может привести к тонким и сложным багам. Две переменные могут выглядеть одинаково, но работают совершенно по-разному.

Переменные класса (Class Variables)

Переменная класса — это переменная, определённая в теле класса, вне методов. Она принадлежит классу, а не отдельным экземплярам.

Характеристики:

  • Определена на уровне класса
  • Общая для всех экземпляров
  • Создаётся один раз при определении класса
  • Изменение одного экземпляра влияет на все остальные
  • Занимает место в памяти один раз
class Dog:
    species = "Canis familiaris"  # Переменная класса
    
    def __init__(self, name):
        self.name = name

# Обе собаки имеют одну и ту же species (из класса)
dog1 = Dog("Buddy")
dog2 = Dog("Max")

print(dog1.species)     # "Canis familiaris"
print(dog2.species)     # "Canis familiaris"
print(Dog.species)      # "Canis familiaris" — доступ через класс

# Они указывают на один и тот же объект
print(dog1.species is dog2.species)  # True

Переменные инициализатора (Instance Variables)

Переменная инициализатора — это переменная, определённая в методе __init__(). Она принадлежит отдельному экземпляру (объекту).

Характеристики:

  • Определена в методе __init__
  • Уникальна для каждого экземпляра
  • Создаётся при создании каждого объекта
  • Изменение одного экземпляра не влияет на другие
  • Занимает памяти больше (копия для каждого объекта)
class Dog:
    def __init__(self, name, breed):
        self.name = name          # Переменная инициализатора
        self.breed = breed        # Переменная инициализатора

dog1 = Dog("Buddy", "Golden Retriever")
dog2 = Dog("Max", "Labrador")

print(dog1.name)  # "Buddy"
print(dog2.name)  # "Max" — разные значения!

dog1.breed = "Husky"  # Изменяем одну собаку
print(dog2.breed)     # "Labrador" — вторая не изменилась

Таблица различий

АспектПеременная классаПеременная инициализатора
Область определенияУровень классаМетод __init__
ПринадлежностьКлассОтдельный экземпляр
Количество копийОдна на всехПо одной на экземпляр
ИзменениеВлияет на всёВлияет только на один
ДоступClassName.var или self.varТолько self.var
СозданиеПри определении классаПри создании экземпляра
ПамятьЭкономнееБольше памяти
ИспользованиеКонстанты, общие данныеУникальные данные

Классический баг: Переменные класса с mutable типами

Это одна из самых коварных ошибок! Если переменная класса — это список или словарь, все экземпляры разделяют один и тот же объект.

# ПЛОХО — скрытый баг!
class ShoppingCart:
    items = []  # Переменная класса (ОПАСНО!)
    
    def __init__(self, customer_name):
        self.customer_name = customer_name
    
    def add_item(self, item):
        self.items.append(item)  # Добавляет в ОБЩИЙ список

cart1 = ShoppingCart("Alice")
cart2 = ShoppingCart("Bob")

cart1.add_item("Apple")
print(cart1.items)  # ["Apple"]
print(cart2.items)  # ["Apple"] — БАГ! В корзине Bob тоже есть Apple!

# Объяснение:
# cart1.items и cart2.items указывают на ОД И НОТ объект!
print(cart1.items is cart2.items)  # True — один и тот же список

Правильный способ: Используйте инициализатор

# ХОРОШО — каждый экземпляр имеет свой список
class ShoppingCart:
    def __init__(self, customer_name):
        self.customer_name = customer_name
        self.items = []  # Переменная инициализатора — свой список
    
    def add_item(self, item):
        self.items.append(item)

cart1 = ShoppingCart("Alice")
cart2 = ShoppingCart("Bob")

cart1.add_item("Apple")
print(cart1.items)  # ["Apple"]
print(cart2.items)  # [] — корзина Bob пуста, как и должно быть

# Объяснение:
# cart1.items и cart2.items — РАЗНЫЕ объекты
print(cart1.items is cart2.items)  # False

Практические примеры

Пример 1: Счётчик экземпляров

class User:
    total_users = 0  # Переменная класса
    
    def __init__(self, name):
        self.name = name  # Переменная инициализатора
        User.total_users += 1

user1 = User("Alice")
user2 = User("Bob")
user3 = User("Charlie")

print(User.total_users)  # 3
print(user1.total_users) # 3 (также доступна через экземпляр)

Пример 2: Конфигурация vs состояние

class DatabaseConnection:
    # Переменные класса — общая конфигурация
    timeout = 30
    max_retries = 3
    
    def __init__(self, host, port):
        # Переменные инициализатора — уникальное состояние
        self.host = host
        self.port = port
        self.connection_id = None
        self.is_connected = False

db1 = DatabaseConnection("localhost", 5432)
db2 = DatabaseConnection("prod.server.com", 5432)

# Конфигурация общая
print(db1.timeout)  # 30
print(db2.timeout)  # 30

# Состояние разное
print(db1.host)     # "localhost"
print(db2.host)     # "prod.server.com"

Пример 3: Когда переменная класса полезна

class Config:
    # Переменные класса — константы, общие для всех
    APP_NAME = "MyApp"
    VERSION = "1.0.0"
    DEBUG = True
    MAX_CONNECTIONS = 100

config1 = Config()
config2 = Config()

# Все читают одну и ту же конфигурацию
print(config1.VERSION)   # "1.0.0"
print(config2.VERSION)   # "1.0.0"
print(Config.VERSION)    # "1.0.0"

# Изменение через класс влияет на всех
Config.DEBUG = False
print(config1.DEBUG)  # False
print(config2.DEBUG)  # False

Порядок поиска атрибутов (Attribute Lookup)

Когда вы обращаетесь к self.var, Python ищет переменную в таком порядке:

class Example:
    class_var = "класс"
    
    def __init__(self):
        self.instance_var = "экземпляр"

obj = Example()

# 1. Сначала ищет в экземпляре
print(obj.instance_var)  # "экземпляр" — найдено в __dict__ экземпляра

# 2. Потом в классе
print(obj.class_var)     # "класс" — найдено в классе

# 3. Если присвоить, создаст в экземпляре
obj.class_var = "экземпляр"
print(obj.class_var)     # "экземпляр" (из экземпляра)
print(Example.class_var) # "класс" (в классе осталось прежнее)

Правила использования

Используйте переменные класса для:

  • Констант (MAX_SIZE, VERSION)
  • Конфигурации
  • Счётчиков (total_instances)
  • Immutable значений (строки, числа, tuples)

Используйте переменные инициализатора для:

  • Состояния экземпляра
  • Данных, которые должны быть уникальными
  • Mutable объектов (списки, словари, наборы)
  • Параметров конструктора
# ПРАВИЛЬНО
class BankAccount:
    # Класс
    currency = "USD"
    interest_rate = 0.05
    
    def __init__(self, owner, initial_balance):
        # Экземпляр
        self.owner = owner
        self.balance = initial_balance
        self.transactions = []  # ОБЯЗАТЕЛЬНО в __init__!

Заключение

Переменные класса — общие для всех экземпляров, полезны для констант и конфигурации.

Переменные инициализатора — уникальны для каждого экземпляра, полезны для состояния.

Основное правило: всегда используйте __init__ для mutable объектов (списки, словари), иначе получите тонкие баги, которые сложно найти.