Нашли ошибку?
Скачать чистый
урок 2 из 4≈ 15 минупражнения: 0 из 3

Методы класса и статические методы

@classmethod получает класс вместо экземпляра — так пишут альтернативные конструкторы: Product.from_line(...). @staticmethod — вспомогательная функция внутри класса.

Python запустится при первом выполнении

Товары в кассу часто приходят не по одному вызову Product("Латте", 220), а строками из файла ("Латте;220") или словарями из формы. Разбирать строку перед каждым созданием экземпляра — неудобно, и делать это в __init__ тоже не хочется: тогда обычный вызов Product("Латте", 220) перестанет работать. Нужен второй способ создать экземпляр — альтернативный конструктор.

@classmethod

Метод с декоратором @classmethod — метод класса (class method). Первым параметром он получает не экземпляр, а сам класс — по соглашению этот параметр называют cls. Вызывают его через класс: Product.from_line("Латте;220"). Внутри cls(...) — то же, что вызов класса, то есть создание экземпляра:

[ ]Товар из строки
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __repr__(self):
        return f"{type(self).__name__}({self.name!r}, {self.price})"

    @classmethod
    def from_line(cls, line):
        name, price = line.split(";")
        return cls(name, int(price))


print(Product.from_line("Латте;220"))
print([Product.from_line(line) for line in ["Чай;120", "Раф;260"]])
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
Product('Латте', 220)
[Product('Чай', 120), Product('Раф', 260)]
[ ]Упражнение 1Напиток из строки решено

Допишите класс Drink: метод класса from_line(line) создаёт напиток из строки вида "Латте;220;300" — название, цена и объём через точку с запятой. Цена и объём — целые числа.

class Drink:
    def __init__(self, name, price, volume):
        self.name = name
        self.price = price
        self.volume = volume

    def __repr__(self):
        return f"{type(self).__name__}({self.name!r}, {self.price}, {self.volume})"

    @classmethod
    def from_line(cls, line):
        ...

Почему cls, а не имя класса

Метод класса наследуется. Если подкласс вызывает from_line, то cls — это подкласс, и создаётся экземпляр подкласса. А Drink(...), записанный внутри метода явно, всегда создаёт Drink — даже когда вызвали SeasonalDrink.from_line(...):

[ ]cls — тот класс, через который вызвали
class SeasonalProduct(Product):
    pass


print(SeasonalProduct.from_line("Глинтвейн;300"))
print(type(SeasonalProduct.from_line("Глинтвейн;300")).__name__)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
SeasonalProduct('Глинтвейн', 300)
SeasonalProduct
[ ]Упражнение 2Покупатель из формы решено

Допишите класс Customer: метод класса from_dict(data) создаёт покупателя из словаря {"name": "Анна", "phone": "+7 900 111-22-33"}. Если в словаре нет ключа "points", баллов 0, иначе — значение из словаря.

class Customer:
    def __init__(self, name, phone, points=0):
        self.name = name
        self.phone = phone
        self.points = points

    @classmethod
    def from_dict(cls, data):
        ...

@staticmethod

Иногда функция логически относится к классу, но ей не нужны ни экземпляр, ни класс — например, проверка формата телефона. Такую функцию объявляют внутри класса с декоратором @staticmethod — это статический метод (static method): без self и без cls. Вызывают его через класс или экземпляр:

[ ]Проверка цены из строки
class PriceTag:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    @staticmethod
    def parse_price(text):  # ни self, ни cls
        return int(text.replace("₽", "").strip())

    @classmethod
    def from_text(cls, name, text):
        return cls(name, cls.parse_price(text))


print(PriceTag.parse_price("220 ₽"))
print(PriceTag.from_text("Латте", "240 ₽").price)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
220
240
[ ]Упражнение 3Проверка телефона решено

Допишите в класс Customer статический метод normalize_phone(phone): он оставляет в строке только цифры и возвращает их строкой, если цифр ровно 11, а иначе выбрасывает ValueError. Например, Customer.normalize_phone("+7 (900) 111-22-33") — "79001112233". Скопируйте класс из упражнения выше.

class Customer:
    # скопируйте сюда __init__ и from_dict из упражнения выше

    @staticmethod
    def normalize_phone(phone):
        ...

Какой метод выбрать

Метод Первый параметр Когда
обычный self — экземпляр работает с данными экземпляра
@classmethod cls — класс создаёт экземпляры (альтернативный конструктор) или работает с атрибутами класса
@staticmethod нет вспомогательная функция, которая по смыслу относится к классу
Вопрос

Какой метод нужен для Order.from_receipt(text), который разбирает текст чека и возвращает новый заказ?

Нашли ошибку?