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

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

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

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

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

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

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

Переменная экземпляра (instance variable) — это атрибут, который принадлежит конкретному объекту класса. Каждый экземпляр имеет свою копию переменной, отличную от других объектов. Давайте разберёмся в различных способах создания и управления ими.

Основной способ: в методе __init__

1. Стандартное создание переменных экземпляра

class Dog:
    def __init__(self, name: str, age: int):
        # Переменные экземпляра создаются в __init__
        self.name = name
        self.age = age
        self.tricks = []  # Пустой список для каждого экземпляра

# Создание экземпляров
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)

# У каждого экземпляра своя копия переменных
print(dog1.name)  # "Buddy"
print(dog2.name)  # "Max"

# Изменение переменной в одном экземпляре не влияет на другой
dog1.age = 4
print(dog2.age)  # Всё ещё 5

# Проверка: переменные хранятся в __dict__
print(dog1.__dict__)  # {'name': 'Buddy', 'age': 4, 'tricks': []}
print(dog2.__dict__)  # {'name': 'Max', 'age': 5, 'tricks': []}

Разница между переменными экземпляра и переменными класса

2. Переменные класса vs. Переменные экземпляра

class Animal:
    species = "Unknown"  # ПЕРЕМЕННАЯ КЛАССА - общая для всех экземпляров
    
    def __init__(self, name: str):
        self.name = name  # ПЕРЕМЕННАЯ ЭКЗЕМПЛЯРА - для каждого объекта
        self.age = 0      # ПЕРЕМЕННАЯ ЭКЗЕМПЛЯРА

# Переменная класса доступна через класс и через экземпляры
print(Animal.species)  # "Unknown"

dog = Animal("Rex")
print(dog.species)  # "Unknown" (берётся из класса)

# Но если присвоить значение через экземпляр - создаётся переменная экземпляра
dog.species = "Canine"  # Создана переменная экземпляра!

print(dog.species)  # "Canine" (теперь это переменная экземпляра)
print(Animal.species)  # "Unknown" (переменная класса не изменилась)

# Другой экземпляр не видит изменения
cat = Animal("Whiskers")
print(cat.species)  # "Unknown" (всё ещё переменная класса)

# ОПАСНОСТЬ: изменяемые переменные класса
class BadExample:
    shared_list = []  # ОБЩИЙ список для всех экземпляров!
    
    def __init__(self, value):
        self.value = value

obj1 = BadExample(1)
obj2 = BadExample(2)

obj1.shared_list.append("data")  # Влияет на ВСЕ экземпляры!

print(obj2.shared_list)  # ["data"] - НЕОЖИДАННО!

Различные типы переменных экземпляра

3. Инициализация различных типов данных

class Person:
    def __init__(self, name: str, age: int, hobbies: list = None):
        # Примитивные типы
        self.name = name
        self.age = age
        self.salary = 0.0
        self.is_active = True
        
        # Контейнеры - ВАЖНО: используйте дефолты правильно!
        self.hobbies = hobbies if hobbies is not None else []
        self.skills = set()
        self.metadata = {}
        
        # Сложные объекты
        self.created_at = None
        self.profile_picture = None

# Использование
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)

person1.hobbies.append("reading")
print(person2.hobbies)  # [] - отдельная копия!

# ОПАСНОСТЬ: использование mutable дефолтов
class BadDefault:
    def __init__(self, name: str, items: list = []):  # ОПАСНО!
        self.name = name
        self.items = items

obj1 = BadDefault("obj1")
obj2 = BadDefault("obj2")

obj1.items.append("item")
print(obj2.items)  # ["item"] - общий список для всех!

# ПРАВИЛЬНО:
class GoodDefault:
    def __init__(self, name: str, items: list = None):
        self.name = name
        self.items = items if items is not None else []

Динамическое создание переменных экземпляра

4. Добавление переменных после инициализации

class FlexibleClass:
    def __init__(self, name: str):
        self.name = name

obj = FlexibleClass("test")

# В Python можно добавлять атрибуты динамически!
obj.age = 25
obj.email = "user@example.com"

print(obj.__dict__)  # {'name': 'test', 'age': 25, 'email': 'user@example.com'}

# Проверка наличия атрибута
print(hasattr(obj, 'age'))  # True
print(hasattr(obj, 'salary'))  # False

# Получение атрибута с дефолтом
age = getattr(obj, 'age', 0)  # 25
salary = getattr(obj, 'salary', 0)  # 0 (дефолт)

# Удаление атрибута
del obj.email
print(hasattr(obj, 'email'))  # False

Использование свойств (Properties)

5. Контролируемый доступ к переменным

class BankAccount:
    def __init__(self, owner: str, initial_balance: float = 0):
        self._owner = owner
        self._balance = initial_balance  # Приватная переменная (по соглашению)
    
    @property
    def balance(self) -> float:
        """Получить баланс (читаемо)"""
        return self._balance
    
    @balance.setter
    def balance(self, amount: float):
        """Установить баланс (с проверкой)"""
        if amount < 0:
            raise ValueError("Balance cannot be negative")
        self._balance = amount
    
    @property
    def owner(self) -> str:
        """Получить владельца (только чтение)"""
        return self._owner

# Использование
account = BankAccount("Alice", 1000)

# Используется как обычная переменная
print(account.balance)  # 1000

# Но на самом деле вызывает setter с проверкой
account.balance = 2000  # OK
account.balance = -100  # ValueError: Balance cannot be negative

# owner - только чтение
print(account.owner)  # "Alice"
# account.owner = "Bob"  # AttributeError: can't set attribute

Использование __slots__ для оптимизации памяти

6. Ограничение переменных экземпляра

class Point:
    # __slots__ ограничивает возможные переменные экземпляра
    __slots__ = ['x', 'y']
    
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

point = Point(1.0, 2.0)

# Разрешённые переменные - OK
point.x = 3.0

# Запрещённые переменные - Error
try:
    point.z = 5.0  # AttributeError: 'Point' object has no attribute 'z'
except AttributeError as e:
    print(f"Error: {e}")

# __slots__ не имеет __dict__
print(hasattr(point, '__dict__'))  # False

# Преимущества:
# - Меньше памяти (нет __dict__)
# - Быстрее доступ
# - Предотвращает ошибки с опечатками

class Rectangle:
    __slots__ = ['width', 'height']
    
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

rect = Rectangle(10, 20)
# rect.widht = 15  # Опечатка - сразу словится!

Инициализация с типизацией (Python 3.7+)

7. Аннотации типов и переменные экземпляра

from typing import List, Optional, Dict
from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int
    email: Optional[str] = None
    tags: List[str] = None
    metadata: Dict[str, str] = None
    
    def __post_init__(self):
        """Вызывается после инициализации dataclass"""
        if self.tags is None:
            self.tags = []
        if self.metadata is None:
            self.metadata = {}

# Использование
user = User(name="Alice", age=30)
print(user.tags)  # []
print(user.metadata)  # {}

# Типизация помогает IDE и type checkers
user.name = "Bob"  # OK - строка
user.age = 25  # OK - число
# user.age = "twenty"  # Type checker выдаст ошибку

Инициализация с методом инициализации

8. Альтернативный способ инициализации

class DatabaseConnection:
    def __init__(self):
        # Инициализируем как None
        self.connection = None
        self.is_connected = False
    
    def connect(self):
        """Инициализировать переменные при подключении"""
        self.connection = self._create_connection()
        self.is_connected = True
        self.last_query = None
    
    def _create_connection(self):
        return "DatabaseConnection()"
    
    def execute_query(self, query: str):
        if not self.is_connected:
            raise RuntimeError("Not connected")
        self.last_query = query
        return f"Executed: {query}"

# Использование
db = DatabaseConnection()
print(db.is_connected)  # False

db.connect()
print(db.is_connected)  # True

result = db.execute_query("SELECT * FROM users")
print(db.last_query)  # "SELECT * FROM users"

Понимание self

9. Что такое self?

class Car:
    def __init__(self, brand: str, model: str):
        self.brand = brand  # self.brand = экземпляру Car
        self.model = model
    
    def describe(self):
        # self относится к конкретному экземпляру
        return f"{self.brand} {self.model}"

car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Civic")

# При вызове метода, Python передаёт экземпляр как первый аргумент
print(car1.describe())  # Эквивалентно Car.describe(car1)
print(car2.describe())  # Эквивалентно Car.describe(car2)

# Можно вызвать явно через класс
print(Car.describe(car1))  # "Toyota Camry"

# self - это просто имя, можно использовать другое (но не стоит)
class WeirdClass:
    def __init__(this, value):  # this вместо self
        this.value = value

obj = WeirdClass(42)
print(obj.value)  # 42 - работает, но плохая практика!

Таким образом, переменные экземпляра создаются в методе __init__ путём присваивания значений атрибутам через self. Каждый экземпляр имеет свою копию этих переменных, независимую от других объектов класса. Используйте типизацию, свойства для контроля доступа и __slots__ для оптимизации памяти.