В этой статье я покажу вам, что такое @staticmethod и @classcmethod, а также почему и когда их следует использовать! Давайте начнем.

Если вы еще не знаете, что такое декоратор и как он работает, то в первую очередь прочитайте эту статью.

Определение:

@статический метод

@staticmethod — это встроенный декоратор, определяющий статический метод в классе Python. Статический метод не получает аргументы ни класса, ни экземпляра, независимо от того, вызывается ли он экземпляром класса или самим классом.

Характеристики

  • Объявляет статический метод в классе.
  • Он не может иметь параметр cls или self.
  • Статический метод не может получить доступ к атрибутам класса или атрибутам экземпляра.
  • Статический метод можно вызвать с помощью ClassName.MethodName(), а также с помощью object.MethodName().
  • Он может возвращать объект класса.

В следующем примере объявляется статический метод:

class Supermarket:
    product = "Milk"  # class attribute
    
    def __init__(self, product, best_before):
        self.best_before = best_before  # instance attribute
        self.product = product
    @staticmethod    
    def normalize_product_name(product):
        product = product.capitalize().strip()        
        return product

Выше класс Supermarket объявляет метод normalize_product_name() как статический метод с помощью декоратора @staticmethod. Обратите внимание, что он не может иметь параметр self или cls.

Статический метод можно вызвать с помощью ClassName.MethodName() или object.MethodName().

>>> norm_product = Supermarket.normalize_product_name("milk  ")
'Milk'
>>> obj = Supermarket("Bread", "2022-05-18")
>>> obj.normalize_product_name("milk  ")
'Milk'

@классметод

@classmethod — это встроенный декоратор, который определяет метод класса в классе в Python. Метод класса получает только аргументы класса. Метод класса можно вызвать с помощью ClassName.MethodName() или object.MethodName().

@classmethod является альтернативой функции classmethod(). Рекомендуется использовать декоратор @classmethod вместо функции, потому что это просто синтаксический сахар.

Характеристики

  • Объявляет метод класса.
  • Первый параметр должен быть cls, который можно использовать для доступа к атрибутам класса.
  • Метод класса может обращаться только к атрибутам класса, но не к атрибутам экземпляра.
  • Метод класса можно вызвать с помощью ClassName.MethodName(), а также с помощью object.MethodName().
  • Он может возвращать объект класса.

В следующем примере объявляется метод класса:

class Supermarket:
    product = "Milk"  # class attribute
    
    def __init__(self, product, best_before):
        self.best_before = best_before  # instance attribute
        self.product = product
    @classmethod    
    def get_product(cls):
        print("product=" + cls.product)

Выше класс Supermarket содержит атрибут класса product и атрибут экземпляра best_before. Метод get_product() украшен декоратором @classmethod, что делает его методом класса, который можно вызывать с помощью метода Supermarket.get_product(). Обратите внимание, что первым параметром любого метода класса должен быть cls, который можно использовать для доступа к атрибутам класса. Вы можете дать любое имя первому параметру вместо cls.

Метод класса можно вызвать с помощью ClassName.MethodName() или object.MethodName().

>>> Supermarket.get_product()
'product=Milk'
>>> obj = Supermarket()
>>> obj.get_product()
'product=Milk'

Но в методе класса вы не можете использовать атрибут экземпляра:

class Supermarket:
    product = "Milk"  # class attribute
    
    def __init__(self, product, best_before):
        self.best_before = best_before  # instance attribute
        self.product = product
    @classmethod    
    def get_product(cls):
        print(f"product={cls.product}, age={cls.best_before}")

>>> Supermarket.get_product()
AttributeError: type object 'Supermarket' has no attribute 'best_before'

Когда следует использовать статические методы?

1. Группировка полезной функции в класс

Статические методы имеют ограниченный вариант использования, поскольку, подобно методам класса или любым другим методам внутри класса, они не могут получить доступ к свойствам самого класса.

Однако, когда вам нужна служебная функция, которая не обращается ни к каким свойствам класса, но имеет смысл, что она принадлежит классу, мы используем статические функции.

Например, вы добавили функцию, которая меняет формат срока годности:

from datetime import datetime

class Supermarket:    
    def __init__(self, product, best_before):
        self.best_before = "2022-05-18"
        self.product = "Milk"
    
    @staticmethod    
    def change_date_format(best_before):
        best_before = datetime.strptime(best_before, "%Y-%m-%d")
        best_before = best_before.strftime("%d-%m-%Y")
        return best_before

>>> Supermarket.change_date_format("2022-08-06")
'06-08-2022'

Это статический метод, поскольку ему не требуется доступ к каким-либо свойствам самого Supermarket, а требуются только параметры.

2. Наличие единой реализации

from datetime import datetime

class Supermarket:    
    def __init__(self, product, best_before):
        self.best_before = best_before
        self.product = product
        
    def get_best_before_date(self):
        return self.best_before
    
    @staticmethod    
    def change_date_format(best_before):
        best_before = datetime.strptime(best_before, "%Y-%m-%d")
        best_before = best_before.strftime("%d-%m-%Y")
        return best_before

class GroceryStore(Supermarket):
    def get_best_before_date(self):
        return Supermarket.change_date_format(self.best_before)
    
    
>>> supermarket = Supermarket("Milk", "2022-05-18")
>>> grocery = GroceryStore("Milk", "2022-05-18")
>>> supermarket.get_best_before_date()
'2022-05-18'
>>> grocery.get_best_before_date()
'18-05-2022'

Когда следует использовать метод класса?

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

1. Заводские методы

Фабричные методы — это те методы, которые возвращают объект класса для различных вариантов использования. Например:

class Supermarket:    
    def __init__(self, product, best_before):
        self.best_before = "2022-05-18"
        self.product = "Milk"
    
    @classmethod    
    def add_product(cls):
        return cls("Bread", "2022-05-29")
>>> obj = Supermarket.add_product()
>>> obj.product
'Milk'
>>> obj.best_before
'2022-05-18'

В этом случае функция add_product() просто создает новый объект класса (новый продукт и его срок годности).

2. Правильное создание экземпляра в наследовании

Всякий раз, когда вы наследуете класс от реализации фабричного метода как метода класса, это гарантирует правильное создание экземпляра производного класса.

Вы можете создать статический метод, но создаваемый им объект всегда будет жестко запрограммирован как базовый класс.

Но когда вы используете метод класса, он создает правильный экземпляр производного класса.

class Supermarket:
    product_price = {"Milk": 1}
    def __init__(self, product, best_before):
        self.best_before = "2022-05-18"
        self.product = "Milk"
    @staticmethod
    def add_import_product(product, best_before):
        return Supermarket(product, best_before)
    @classmethod    
    def add_product(cls, product, best_before):
        return cls(product, best_before)

class GroceryStore(Supermarket):
    product_price = {"Milk": 2}
grocery1 = GroceryStore.add_import_product("Bread", "2022-06-05")
isinstance(grocery1, GroceryStore)
>>> False
grocery2 = GroceryStore.add_product("Apple", "2022-06-10")
isinstance(grocery2, GroceryStore)
>>> True

Здесь, используя статический метод для создания экземпляра класса, мы хотим, чтобы мы жестко закодировали тип экземпляра во время создания.

Это явно вызывает проблему при наследовании от Supermarket до GroceryStore.

Метод add_import_product возвращает не объект Grocerystore, а объект базового класса Supermarkets.

Это нарушает парадигму ООП. Использование метода класса как add_product() может обеспечить ООП-образность кода, поскольку он принимает первый параметр как сам класс и вызывает его фабричный метод.

Заключение:

В этой статье мы разобрали, что такое @staticmethod и @classcmethod, и узнали, как, где и зачем их использовать:

  • Обычно мы используем @classmethod для создания фабричных методов. Фабричные методы возвращают объекты класса для различных вариантов использования.
  • Обычно мы используем статические методы для создания и группировки служебных функций.

Делитесь своими мыслями и мнением в комментариях, жмите "Хлопать", если статья была для вас полезной и интересной. Нажмите «Подписаться», чтобы всегда получать полезные статьи!