← Назад к вопросам
В чем разница декораторов @staticmethod и @classmethod с точки зрения принимаемых аргументов?
1.3 Junior🔥 221 комментариев
#Python Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между @staticmethod и @classmethod в Python
Это фундаментальное различие в Python, касающееся того, какие аргументы получает метод.
Быстрая таблица сравнения
| Аспект | @staticmethod | @classmethod | Обычный метод |
|---|---|---|---|
| Первый аргумент | Ничего | cls (класс) | self (экземпляр) |
| Доступ к данным | ❌ Нельзя | ✅ К данным класса | ✅ К данным экземпляра |
| Переопределение в подклассе | ❌ Нет | ✅ Да | ✅ Да |
| Использование | Утилиты | Альтернативные конструкторы | Основной код |
1. @staticmethod — статический метод
class Math:
@staticmethod
def add(a, b):
"""Статический метод: НЕ получает ни self ни cls"""
return a + b
# Вызов
result = Math.add(5, 3) # 8
# Можно вызвать и на экземпляре (но обычно так не делают)
math_obj = Math()
result = math_obj.add(5, 3) # Также 8, но self НЕ передаётся
# Характеристики:
# 1. Не имеет доступа к self (экземпляру)
# 2. Не имеет доступа к cls (классу)
# 3. Это просто функция внутри класса
# 4. Логически принадлежит классу, но не использует его
2. @classmethod — метод класса
class Counter:
count = 0 # Переменная класса
def __init__(self, name):
self.name = name
Counter.count += 1
@classmethod
def get_count(cls):
"""Метод класса: ПОЛУЧАЕТ cls как первый аргумент"""
return cls.count
@classmethod
def from_string(cls, data):
"""Альтернативный конструктор"""
# Получает класс, может создать экземпляр
name = data.split(':')[0]
return cls(name) # Вызовет __init__
# Использование
c1 = Counter('Alice')
c2 = Counter('Bob')
print(Counter.get_count()) # 2 - получает доступ к cls.count
print(c1.get_count()) # 2 - тоже работает (cls = Counter)
# Альтернативный конструктор
c3 = Counter.from_string('Charlie:30')
print(Counter.get_count()) # 3
# Характеристики:
# 1. ПОЛУЧАЕТ cls как первый аргумент
# 2. cls = класс, в котором был вызван метод
# 3. Может создавать экземпляры (cls(...))
# 4. Может изменять данные класса
# 5. Переопределяется в подклассах (polymorphism!)
3. Практический пример: различие в полиморфизме
class Animal:
"""Base класс"""
kind = 'Unknown'
@staticmethod
def make_sound():
"""Статический метод - НЕ переопределяется в подклассах"""
return "Some sound"
@classmethod
def describe(cls):
"""Метод класса - ДА переопределяется через cls"""
return f"This is a {cls.kind}"
def instance_method(self):
"""Обычный метод"""
return f"I am {self.__class__.__name__}"
class Dog(Animal):
"""Подкласс"""
kind = 'Dog'
# ❌ Не нужно переопределять staticmethod
# @staticmethod
# def make_sound():
# return "Woof"
# Если переопределишь - будет отдельный метод, не override
# ✅ Переопределяем classmethod
@classmethod
def describe(cls):
return f"This is a {cls.kind} (bark bark)"
class Cat(Animal):
"""Другой подкласс"""
kind = 'Cat'
@classmethod
def describe(cls):
return f"This is a {cls.kind} (meow meow)"
# Результаты
print(Animal.make_sound()) # "Some sound"
print(Dog.make_sound()) # "Some sound" - ОДИНАКОВО!
print(Cat.make_sound()) # "Some sound" - ОДИНАКОВО!
print(Animal.describe()) # "This is a Unknown"
print(Dog.describe()) # "This is a Dog (bark bark)" ✅ Переопределено!
print(Cat.describe()) # "This is a Cat (meow meow)" ✅ Переопределено!
# ✅ classmethod позволяет полиморфизм
# ❌ staticmethod НЕ позволяет полиморфизм
4. Визуализация: что такое cls?
class Parent:
@classmethod
def show_class(cls):
print(f"cls = {cls}")
print(f"cls.__name__ = {cls.__name__}")
class Child(Parent):
pass
# cls изменяется в зависимости от того, где вызвали!
Parent.show_class()
# cls = <class 'Parent'>
# cls.__name__ = Parent
Child.show_class()
# cls = <class 'Child'> ✅ ДРУГОЙ КЛАСС!
# cls.__name__ = Child
child_instance = Child()
child_instance.show_class()
# cls = <class 'Child'> ✅ ВСЕ ЕЩЁ Child (не Child instance!)
# cls.__name__ = Child
# ✅ cls = класс, через который был вызван метод (или экземпляра)
5. Практический пример: Factory Pattern с classmethod
from abc import ABC, abstractmethod
from typing import Type
class Database(ABC):
"""Base класс для разных БД"""
@abstractmethod
def connect(self):
pass
class PostgreSQL(Database):
def connect(self):
print("Connected to PostgreSQL")
class MySQL(Database):
def connect(self):
print("Connected to MySQL")
class SQLite(Database):
def connect(self):
print("Connected to SQLite")
class DatabaseFactory:
"""Factory с classmethod"""
_db_types = {
'postgres': PostgreSQL,
'mysql': MySQL,
'sqlite': SQLite,
}
@classmethod
def create_db(cls, db_type: str) -> Database:
"""Factory method с classmethod"""
db_class = cls._db_types.get(db_type)
if not db_class:
raise ValueError(f"Unknown DB type: {db_type}")
# ✅ Создаёт нужный класс через cls
return db_class() # Или cls._db_types[db_type]()
# Использование
db1 = DatabaseFactory.create_db('postgres')
db1.connect() # Connected to PostgreSQL
db2 = DatabaseFactory.create_db('mysql')
db2.connect() # Connected to MySQL
# classmethod позволяет полиморфный factory pattern
6. Альтернативные конструкторы с classmethod
from datetime import datetime
class User:
def __init__(self, id: int, name: str, email: str):
self.id = id
self.name = name
self.email = email
self.created_at = datetime.now()
# ❌ ПЛОХО: несколько инициализаторов
# def __init__(self, ...):
# def __init__(self, ...):
# Можно только один __init__!
# ✅ ПРАВИЛЬНО: использовать classmethod для альтернативных конструкторов
@classmethod
def from_dict(cls, data: dict):
"""Создать из словаря"""
return cls(
id=data['id'],
name=data['name'],
email=data['email']
)
@classmethod
def from_json_string(cls, json_str: str):
"""Создать из JSON строки"""
import json
data = json.loads(json_str)
return cls.from_dict(data) # Переиспользуем другой конструктор
@classmethod
def from_database_row(cls, row):
"""Создать из строки БД"""
return cls(
id=row[0],
name=row[1],
email=row[2]
)
# Разные способы создания одного объекта
user1 = User(1, 'Alice', 'alice@example.com')
user2 = User.from_dict({
'id': 2,
'name': 'Bob',
'email': 'bob@example.com'
})
user3 = User.from_json_string(
'{"id": 3, "name": "Charlie", "email": "charlie@example.com"}'
)
user4 = User.from_database_row((4, 'David', 'david@example.com'))
# ✅ classmethod позволяет множеств альтернативных конструкторов
7. Когда использовать что
# ✅ @staticmethod когда:
# - Метод логически связан с классом
# - НО не нужен доступ к self или cls
# - Это просто утилита
class StringUtils:
@staticmethod
def is_email_valid(email: str) -> bool:
"""Утилита для проверки email"""
return '@' in email
@staticmethod
def capitalize_words(text: str) -> str:
"""Утилита для капитализации"""
return ' '.join(word.capitalize() for word in text.split())
# ✅ @classmethod когда:
# - Нужен доступ к классу (cls)
# - Создание альтернативных конструкторов
# - Изменение данных класса
# - Factory pattern
# - Хотите переопределение в подклассах (полиморфизм)
class FileHandler:
supported_formats = ['.txt', '.pdf', '.doc']
@classmethod
def add_format(cls, format: str):
"""Добавить поддерживаемый формат"""
cls.supported_formats.append(format)
@classmethod
def is_supported(cls, filename: str) -> bool:
"""Проверить поддержку формата"""
extension = '.' + filename.split('.')[-1]
return extension in cls.supported_formats
# ✅ Обычный метод когда:
# - Нужен доступ к self (данным экземпляра)
# - Основная логика класса
class BankAccount:
def __init__(self, balance: float):
self.balance = balance
def withdraw(self, amount: float):
"""Обычный метод: работает с self.balance"""
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
8. Подробное сравнение аргументов
class Example:
class_var = "I'm class variable"
def __init__(self, value):
self.instance_var = value
# ===== СТАТИЧЕСКИЙ МЕТОД =====
@staticmethod
def static_method(arg1, arg2):
# Аргументы: arg1, arg2 (ничего больше)
# Доступ: не к self, не к cls
# Может делать: только то, что передано в аргументах
return arg1 + arg2
# ===== МЕТОД КЛАССА =====
@classmethod
def class_method(cls, arg1, arg2):
# ПЕРВЫЙ аргумент: cls (АВТОМАТИЧЕСКИ передаётся)
# Остальные: arg1, arg2 (как указаны)
# Доступ: к cls (классу) и его переменным
print(f"cls.class_var = {cls.class_var}")
return f"Created via {cls.__name__}: {arg1} + {arg2}"
# ===== ОБЫЧНЫЙ МЕТОД =====
def instance_method(self, arg1, arg2):
# ПЕРВЫЙ аргумент: self (АВТОМАТИЧЕСКИ передаётся)
# Остальные: arg1, arg2 (как указаны)
# Доступ: к self (экземпляру) и его переменным
print(f"self.instance_var = {self.instance_var}")
return f"Instance method: {arg1} + {arg2}"
# Вызовы и что передаётся
print(Example.static_method(5, 3))
# Аргументы: 5, 3
# Результат: 8
print(Example.class_method(5, 3))
# Аргументы: cls (автоматически Example), 5, 3
# Результат: Created via Example: 5 + 3
obj = Example(42)
print(obj.instance_method(5, 3))
# Аргументы: self (автоматически obj), 5, 3
# Результат: Instance method: 5 + 3
9. Визуальное сравнение сигнатур
@staticmethod
def method(arg1, arg2):
# def method(arg1, arg2):
# Первый аргумент = arg1
# Второй аргумент = arg2
# Никаких автоматических!
@classmethod
def method(cls, arg1, arg2):
# def method(cls, arg1, arg2):
# Первый аргумент = cls (автоматический)
# Второй аргумент = arg1
# Третий аргумент = arg2
# cls передаётся АВТОМАТИЧЕСКИ
def method(self, arg1, arg2):
# def method(self, arg1, arg2):
# Первый аргумент = self (автоматический)
# Второй аргумент = arg1
# Третий аргумент = arg2
# self передаётся АВТОМАТИЧЕСКИ
10. Итоговый вывод
@staticmethod:
- НЕ получает self
- НЕ получает cls
- Получает только явно переданные аргументы
- Не может переопределяться в подклассах (для полиморфизма)
- Используется для утилит
@classmethod:
- ПОЛУЧАЕТ cls как ПЕРВЫЙ аргумент (автоматически)
- Получает остальные аргументы как обычно
- cls = класс, через который был вызван
- МОЖЕТ переопределяться в подклассах (полиморфизм!)
- Используется для альтернативных конструкторов, factory pattern'ов
Обычный метод:
- ПОЛУЧАЕТ self как ПЕРВЫЙ аргумент (автоматически)
- Получает остальные аргументы как обычно
- self = экземпляр, на котором был вызван
- МОЖЕТ переопределяться в подклассах
- Основная логика класса
Ключевое отличие: @staticmethod не получает ничего, @classmethod получает cls, обычный метод получает self.