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

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

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

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

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

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

Устройство класса в Python

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

Базовая структура класса

class Dog:
    # Атрибут класса (общий для всех экземпляров)
    species = "Canis familiaris"
    
    # Метод инициализации (конструктор)
    def __init__(self, name, age):
        # Атрибуты экземпляра
        self.name = name
        self.age = age
    
    # Метод экземпляра
    def speak(self):
        return f"{self.name} говорит Гав"
    
    # Классметод
    @classmethod
    def create_from_string(cls, data):
        name, age = data.split(",")
        return cls(name, int(age))
    
    # Статический метод
    @staticmethod
    def is_valid_age(age):
        return age >= 0

dog = Dog("Rex", 5)
print(dog.speak())  # Rex говорит Гав

Внутреннее устройство класса

Когда Python встречает определение класса, происходит следующее:

1. Класс как dict (словарь)

Внутри класса хранится словарь (__dict__), содержащий все определения:

class Simple:
    x = 10
    def method(self):
        pass

print(Simple.__dict__)
# {x: 10, method: <function>}

# Доступ к атрибутам класса
print(Simple.x)  # 10
print(type(Simple.x))  # <class int>

2. Метакласс (metaclass)

Каждый класс — это экземпляр метакласса (по умолчанию type):

class MyClass:
    pass

print(type(MyClass))  # <class type>
print(isinstance(MyClass, type))  # True

# MyClass это объект, созданный метаклассом type
# type это объект, созданный самим type
print(type(type))  # <class type>

3. MRO (Method Resolution Order)

Порядок поиска методов в иерархии наследования:

class A:
    def method(self):
        return "A"

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):
    pass

print(D.mro())  # [D, B, C, A, object]
print(D().method())  # "B"

# MRO: D -> B -> C -> A -> object

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

class Counter:
    count = 0  # Атрибут класса (общий)
    
    def __init__(self, name):
        self.name = name  # Атрибут экземпляра
        Counter.count += 1
    
    def get_count(self):
        return Counter.count

# Использование
c1 = Counter("c1")
c2 = Counter("c2")

print(c1.count)  # 2 (относится к классу)
print(c2.count)  # 2 (относится к классу)
print(Counter.count)  # 2 (явно класса)

print(c1.name)  # "c1" (атрибут экземпляра)
print(c2.name)  # "c2" (атрибут экземпляра)

Как создаётся класс: процесс определения

Когда интерпретатор встречает class, происходит это:

# Это написано в коде:
class MyClass:
    x = 10
    def method(self):
        return self.x

# Интернально это эквивалентно:
MyClass = type(
    MyClass,  # Имя класса
    (),  # Кортеж базовых классов
    {  # Словарь атрибутов
        x: 10,
        method: lambda self: self.x
    }
)

Специальные методы (dunder methods)

class Person:
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return f"Person: {self.name}"
    
    def __repr__(self):
        return f"Person({self.name})"
    
    def __eq__(self, other):
        return self.name == other.name
    
    def __lt__(self, other):
        return len(self.name) < len(other.name)
    
    def __add__(self, other):
        return f"{self.name} + {other.name}"
    
    def __call__(self, *args):
        return f"Вызвана {self.name} с аргументами {args}"
    
    def __len__(self):
        return len(self.name)

p1 = Person("Alice")
print(str(p1))  # Person: Alice
print(repr(p1))  # Person(Alice)
print(p1 + Person("Bob"))  # Alice + Bob
print(p1())  # Вызвана Alice с аргументами ()
print(len(p1))  # 5

Слоты (slots) для оптимизации памяти

# Без слотов
class RegularClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# Со слотами
class SlottedClass:
    __slots__ = [x, y]
    
    def __init__(self, x, y):
        self.x = x
        self.y = y

# SlottedClass использует меньше памяти
import sys
r = RegularClass(1, 2)
s = SlottedClass(1, 2)

print(sys.getsizeof(r.__dict__))  # ~280 байт
print(sys.getsizeof(s))  # ~56 байт (SlottedClass намного меньше)

Дескрипторы (descriptors)

Это специальный протокол для переопределения доступа к атрибутам:

class Descriptor:
    def __get__(self, obj, objtype=None):
        return obj._value if obj else self
    
    def __set__(self, obj, value):
        obj._value = value
    
    def __delete__(self, obj):
        del obj._value

class MyClass:
    prop = Descriptor()
    
    def __init__(self, value):
        self.prop = value

obj = MyClass(42)
print(obj.prop)  # 42
obj.prop = 100
print(obj.prop)  # 100

Свойства (properties) — частный случай дескрипторов

class Temperature:
    def __init__(self, celsius):
        self._celsius = celsius
    
    @property
    def celsius(self):
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Невозможная температура")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32

temp = Temperature(0)
print(temp.celsius)  # 0
print(temp.fahrenheit)  # 32
temp.celsius = 100
print(temp.fahrenheit)  # 212

Иерархия классов

class Animal:  # Базовый класс
    def speak(self):
        return "Звук"

class Dog(Animal):  # Наследование
    def speak(self):  # Переопределение
        return "Гав"

class GoldenRetriever(Dog):  # Множественное наследование
    def fetch(self):
        return "Принёс мяч"

# Вызов метода базового класса
class Bird(Animal):
    def speak(self):
        return super().speak() + ", но это чирик"  # Чирик, но это чирик

bird = Bird()
print(bird.speak())

Выводы

  1. Классы — это объекты, созданные метаклассом type
  2. Атрибуты класса — общие для всех экземпляров
  3. Атрибуты экземпляра — уникальные для каждого объекта
  4. MRO определяет порядок поиска методов в наследовании
  5. Специальные методы позволяют переопределить поведение операторов
  6. Дескрипторы — мощный механизм для управления атрибутами
  7. Слоты оптимизируют память для классов с фиксированным числом атрибутов