Методы класса в Python: синтаксис, варианты и практические сценарии

Раздел: Объектно-ориентированное программирование -> Объектно-ориентированное программирование

Методы класса в Python: @classmethod

Основным и наиболее распространённым способом объявления метода класса в Python является использование декоратора @classmethod. Такой метод принимает первым аргументом ссылку на сам класс (обычно обозначается cls), а не на экземпляр. Это позволяет методу работать с атрибутами класса и вызывать другие методы класса, оставаясь независимым от конкретного объекта.

class MyClass:
    class_attr = 10

    @classmethod
    def class_method(cls, value):
        cls.class_attr += value
        return cls.class_attr

print(MyClass.class_method(5))  # 15

атрибуты класса python (атрибуты классов и объектов в python)

15

библиотека классов python (библиотека классов в python)

В этом примере class_method изменяет атрибут класса class_attr. Вызов метода возможен как через класс, так и через экземпляр.

Как объявить метод класса без использования синтаксиса декоратора?

Можно воспользоваться встроенной функцией classmethod(). Она принимает обычную функцию и возвращает объект метода класса.

class MyClass:
    class_attr = 0

    def _class_method(cls):
        cls.class_attr += 1
        return cls.class_attr

    alt_method = classmethod(_class_method)

print(MyClass.alt_method())  # 1

метод объекта python (методы объектов в python)

Типичная ошибка: забыть передать функцию, а не её результат. Если написать alt_method = classmethod(_class_method()), то будет вызвана функция с неправильными аргументами и метод не заработает. Нужно передавать саму функцию без скобок.

Как использовать методы класса для создания альтернативных конструкторов?

Это одна из ключевых областей применения @classmethod. Фабричные методы позволяют создавать объекты класса разными способами.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    @classmethod
    def from_tuple(cls, coords):
        return cls(*coords)

    @classmethod
    def from_polar(cls, radius, angle):
        import math
        return cls(radius * math.cos(angle), radius * math.sin(angle))

p1 = Point.from_tuple((3, 4))
p2 = Point.from_polar(5, 0.9273)
print(p1.x, p1.y)  # 3 4
print(p2.x, p2.y)  # 3.000... 4.000...

Python структура объекта (структура объекта в python)

Проблема: если в подклассе переопределён конструктор, фабричные методы должны корректно работать. Использование cls вместо жёсткой ссылки на имя класса позволяет подклассам автоматически возвращать правильный тип.

Как с помощью методов класса изменять атрибуты, общие для всех экземпляров?

Методы класса идеально подходят для модификации состояния, которое хранится на уровне класса (например, счётчики, конфигурации).

class Counter:
    count = 0

    @classmethod
    def increment(cls, delta=1):
        cls.count += delta
        return cls.count

    @classmethod
    def reset(cls):
        cls.count = 0

Counter.increment()
Counter.increment(5)
print(Counter.count)  # 6
Counter.reset()
print(Counter.count)  # 0

Python создание объектов (создание объектов в python)

Важно не путать атрибуты класса и экземпляра. Если изменить self.count внутри метода класса, это создаст локальный атрибут экземпляра и не повлияет на класс. Метод класса должен работать только с cls.

Чем метод класса отличается от статического метода и обычного метода?

Обычный метод (экземпляра) принимает self и имеет доступ к атрибутам конкретного объекта. Статический метод (@staticmethod) не принимает ни self, ни cls и работает как обычная функция внутри класса. Метод класса (@classmethod) принимает cls и может обращаться к атрибутам класса и вызывать другие методы класса.

class Demo:
    @classmethod
    def cm(cls):
        return "classmethod"

    @staticmethod
    def sm():
        return "staticmethod"

    def im(self):
        return "instance method"

print(Demo.cm())  # classmethod
print(Demo.sm())  # staticmethod
print(Demo().im()) # instance method

Ошибка: вызов обычного метода от класса (без экземпляра) приводит к ошибке TypeError, так как self не передан. Метод класса и статический метод можно вызывать как от класса, так и от экземпляра.

- Python call method (вызов метода в python)
- Python класс данных (класс данных в python)
- Class method python (методы классов в python)

Расширенные примеры использования методов класса

Пример
# Пример 1: Паттерн 'Одиночка' с использованием classmethod
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

    @classmethod
    def get_instance(cls):
        if cls._instance is None:
            cls()  # вызовет __new__
        return cls._instance

s1 = Singleton.get_instance()
s2 = Singleton.get_instance()
print(s1 is s2)  # True
True
Пример
# Пример 2: Подсчёт созданных экземпляров через методы класса
class Task:
    total_tasks = 0

    def __init__(self, title):
        self.title = title
        Task.total_tasks += 1  # можно и cls, но проще через Task

    @classmethod
    def task_count(cls):
        return cls.total_tasks

    @classmethod
    def create_empty(cls):
        return cls("Untitled")

t1 = Task("First")
t2 = Task("Second")
t3 = Task.create_empty()
print(Task.task_count())  # 3
3
Пример
# Пример 3: Наследование и проверка принадлежности к подклассу
class Animal:
    species = "Unknown"

    @classmethod
    def get_species(cls):
        return cls.species

    @classmethod
    def is_animal(cls, obj):
        return isinstance(obj, cls)

class Dog(Animal):
    species = "Canis lupus familiaris"

class Cat(Animal):
    species = "Felis catus"

print(Animal.get_species())  # Unknown
print(Dog.get_species())     # Canis lupus familiaris
print(Cat.get_species())     # Felis catus

pet = Dog()
print(Animal.is_animal(pet))  # True (проверка по базовому классу)
print(Dog.is_animal(pet))     # True
print(Cat.is_animal(pet))     # False
Unknown
Canis lupus familiaris
Felis catus
True
True
False
Пример
# Пример 4: Кэширование или общий реестр объектов
class Registered:
    registry = {}

    def __init__(self, name):
        self.name = name
        Registered.registry[name] = self

    @classmethod
    def get_by_name(cls, name):
        return cls.registry.get(name, None)

    @classmethod
    def list_registered(cls):
        return list(cls.registry.keys())

obj_a = Registered("alpha")
obj_b = Registered("beta")
print(Registered.list_registered())  # ['alpha', 'beta']
print(Registered.get_by_name("alpha"))  # <__main__.Registered object ...>
['alpha', 'beta']
<__main__.Registered object at 0x...>
Пример
# Пример 5: Использование classmethod для парсинга данных из разных форматов
class Config:
    def __init__(self, db_host, db_port):
        self.db_host = db_host
        self.db_port = db_port

    @classmethod
    def from_json(cls, json_str):
        import json
        data = json.loads(json_str)
        return cls(data['host'], data['port'])

    @classmethod
    def from_dict(cls, d):
        return cls(d['host'], d['port'])

cfg1 = Config.from_json('{"host":"localhost","port":5432}')
cfg2 = Config.from_dict({'host':'example.com','port':3306})
print(cfg1.db_host)  # localhost
print(cfg2.db_port)  # 3306
localhost
3306

Методы классов в Python - comments

En
Class method python (python)