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

Как реализован конструктор класса в Python?

1.6 Junior🔥 221 комментариев
#Python Core

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

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

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

Реализация конструктора класса в Python

Конструктор в Python реализуется через специальный метод __init__, который вызывается при создании экземпляра класса. Это не совсем конструктор в смысле C++, а скорее инициализатор.

Базовый конструктор

class User:
    def __init__(self, name: str, email: str):
        """Конструктор (инициализатор) класса."""
        self.name = name
        self.email = email
    
    def __repr__(self):
        return f"User(name={self.name}, email={self.email})"

# Создание экземпляра
user = User("Alice", "alice@example.com")
print(user)  # User(name=Alice, email=alice@example.com)

Процесс:

  1. Python вызывает __new__ (создание объекта в памяти)
  2. Python вызывает __init__ (инициализация атрибутов)
  3. Возвращается готовый объект

new и init вместе

Это два разных метода:

class MyClass:
    def __new__(cls, *args, **kwargs):
        """Создание объекта (allocation)."""
        print("__new__ called")
        instance = super().__new__(cls)
        return instance
    
    def __init__(self):
        """Инициализация объекта (initialization)."""
        print("__init__ called")
        self.value = 42

obj = MyClass()
# Вывод:
# __new__ called
# __init__ called

Отличия:

  • __new__ — статический метод, создаёт объект, получает cls
  • __init__ — обычный метод, инициализирует объект, получает self
  • __new__ ДОЛЖЕН вернуть экземпляр класса
  • __init__ ДОЛЖЕН вернуть None

Конструктор с параметрами по умолчанию

class Point:
    def __init__(self, x: float = 0.0, y: float = 0.0):
        self.x = x
        self.y = y
    
    def distance(self) -> float:
        return (self.x ** 2 + self.y ** 2) ** 0.5

# Использование
p1 = Point(3, 4)
print(p1.distance())  # 5.0

p2 = Point()  # Значения по умолчанию
print(p2.distance())  # 0.0

p3 = Point(y=5)  # x=0 по умолчанию
print(p3.distance())  # 5.0

Конструктор в наследовании

class Animal:
    def __init__(self, name: str):
        self.name = name
        print(f"Animal.__init__ called for {name}")

class Dog(Animal):
    def __init__(self, name: str, breed: str):
        # ❌ Правильно ошибка: не вызвали __init__ родителя
        self.breed = breed  # AttributeError: name не инициализирован

# ✅ Правильно: вызвать родителя
class Dog(Animal):
    def __init__(self, name: str, breed: str):
        super().__init__(name)  # Инициализация родителя
        self.breed = breed
    
    def describe(self):
        return f"{self.name} is a {self.breed}"

dog = Dog("Buddy", "Golden Retriever")
print(dog.describe())  # Buddy is a Golden Retriever

Множественное наследование:

class Mixin:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.mixin_attr = "from mixin"

class Base:
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.base_attr = "from base"

class Child(Mixin, Base):
    def __init__(self, name: str):
        self.name = name
        super().__init__()

child = Child("Test")
print(child.name)         # Test
print(child.mixin_attr)   # from mixin
print(child.base_attr)    # from base

Конструкторы через classmethod

Альтернативные способы создания объектов:

from datetime import datetime

class Person:
    def __init__(self, name: str, birth_year: int):
        self.name = name
        self.birth_year = birth_year
    
    @classmethod
    def from_age(cls, name: str, age: int):
        """Альтернативный "конструктор" через возраст."""
        birth_year = datetime.now().year - age
        return cls(name, birth_year)
    
    @classmethod
    def from_string(cls, person_str: str):
        """Парсинг из строки."""
        name, year = person_str.split(",")
        return cls(name, int(year))
    
    def age(self) -> int:
        return datetime.now().year - self.birth_year

# Использование
p1 = Person("Alice", 1990)
p2 = Person.from_age("Bob", 30)
p3 = Person.from_string("Charlie,1985")

print(p2.age())  # ~30

Конструктор с проверкой данных

class BankAccount:
    def __init__(self, account_number: str, balance: float = 0):
        # Валидация
        if not account_number or len(account_number) < 5:
            raise ValueError("Invalid account number")
        
        if balance < 0:
            raise ValueError("Balance cannot be negative")
        
        self.account_number = account_number
        self.balance = balance
    
    def deposit(self, amount: float):
        if amount <= 0:
            raise ValueError("Deposit amount must be positive")
        self.balance += amount

# Использование
try:
    acc = BankAccount("12345", -100)  # ValueError!
except ValueError as e:
    print(f"Error: {e}")

acc = BankAccount("12345", 100)
acc.deposit(50)
print(acc.balance)  # 150

Конструктор со сложной инициализацией

class Database:
    _instance = None
    
    def __new__(cls, connection_string: str):
        """Синглтон паттерн."""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance
    
    def __init__(self, connection_string: str):
        if self._initialized:
            return  # Не инициализируем дважды
        
        self.connection_string = connection_string
        self._connect()
        self._initialized = True
    
    def _connect(self):
        print(f"Connecting to {self.connection_string}")

# Использование
db1 = Database("postgres://localhost")
db2 = Database("mysql://localhost")  # Подключение не происходит

print(db1 is db2)  # True (один и тот же объект)

Конструктор с @dataclass

Модульный способ (Python 3.7+):

from dataclasses import dataclass, field
from typing import List

@dataclass
class Student:
    name: str
    age: int
    grades: List[float] = field(default_factory=list)
    
    def average_grade(self) -> float:
        return sum(self.grades) / len(self.grades) if self.grades else 0

# Автоматически создаётся __init__
student = Student("Alice", 20, [4.5, 4.0, 3.8])
print(student.average_grade())  # 4.1

# __repr__, __eq__ и др. автоматически
print(student)  # Student(name='Alice', age=20, grades=[4.5, 4.0, 3.8])

Атрибуты класса vs экземпляра

class Counter:
    count = 0  # Атрибут класса
    
    def __init__(self, name: str):
        Counter.count += 1  # Изменение атрибута класса
        self.name = name    # Атрибут экземпляра
        self.id = Counter.count

c1 = Counter("First")
c2 = Counter("Second")

print(c1.id)  # 1
print(c2.id)  # 2
print(Counter.count)  # 2

slots для оптимизации памяти

class Point:
    __slots__ = ['x', 'y']  # Только эти атрибуты
    
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

p = Point(3, 4)
print(p.x)  # 3
p.z = 5  # AttributeError: 'Point' object has no attribute 'z'

Лучшие практики

  • Всегда вызывайте super().__init__() в наследовании
  • Валидируйте параметры в init
  • Используйте type hints для ясности
  • Документируйте параметры конструктора через docstring
  • Избегайте сложной логики в конструкторе
  • Используйте @dataclass для простых классов
  • Никогда не создавайте побочные эффекты в init (вроде HTTP запросов)
# ✅ Правильно
class Config:
    def __init__(self, path: str):
        """Инициализирует конфиг из файла.
        
        Args:
            path: Путь к файлу конфигурации
        """
        if not os.path.exists(path):
            raise FileNotFoundError(f"Config file not found: {path}")
        
        self.path = path
        self.data = self._load_config()
    
    def _load_config(self) -> dict:
        with open(self.path) as f:
            return json.load(f)

Конструктор в Python — это мощный инструмент для инициализации объектов и установки начального состояния.