Статические методы
Limera1n
Сегодня мы посмотрим, как объявлять статические методы. Иногда мы хотим определить статические методы, которые сидят внутри класса, но для их вызова не обязательно создавать инстанцию такого класса. Собственно, посмотрим на практике, как это все выглядит и все станет понятно.

В начале приведем кое-какой тест, касающийся статических атрибутов. Заведем простой класс, объявим в нем атрибут на уровне класса, которому мы присвоим единицу. Важно то, что статические атрибуты можно вызывать, как через имя класса, так и через инстанцию класса. К чему это ведет? Давайте глянем.

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

В первом случае у нас 2, а во втором 1. Почему же так происходит? Дело в том, что вот такой доступ к Х - t1.x = 2, и запись в него создает атрибут на уровне инстанции и мы туда записываем 2. То есть эти в икса - два разных атрибута (t1.x и StaticTest.x).

Вот тут уже мы поменяли StaticTest.x и получили результат. Такое присваивание работает уже на уровне класса.
Теперь что касается статических методов. Иногда мы будем писать классы, в которых нет состояния, то есть там нет атрибутов, там есть только набор методов, просто мы их объединяем в классе, как в контейнере.
Есть 2 способа создать статические методы. Для того, чтобы посмотреть на разницу между двумя этими подходами мы сразу начнем с примера, который продемонстрирует именно эту разницу.

Предположим у нас есть класс Date, он принимает self, месяц, день и год. Создадим метод, который назовем display уровня инстанции, он будет возвращать месяц, день и год. В Python конструктор может быть только один, кроме того он не имеет имени. Поэтому мы в некоторых случаях прибегаем к статическим методам, которые используем в качестве конструкторов. В чем преимущество статических методов над конструкторами? В том, что статические методы имеют имя. Например, что если мы ходим дать клиенту нашего класса Date создавать дату исключительно в 2000 году и дать для этого описательное имя millenium? Смотрим:

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

В выводе все одинаково, так в чем же все-таки разница? А вот в чем.

Сделаем небольшую хитрость, создадим класс DateTime, который наследуется от нашего класса Date. И вот он переопределит нам метод display, и будет выводить данные немного по другому. По скольку это DateTime - у него есть время, и по умолчанию время мы будем ставить в полночь. Тут мы через isinstance узнаем, принадлежит ли dt1 и dt2 к DateTime. Так что здесь происходит? Происходит здесь то, что когда мы вызываем конструктор DateTime напрямую мы получаем инстанцию DateTime конструируя его через конструктор Date и получаем тип DateTime, а вот вызывая millenium_s, это статик метод, который не имеет cls в качестве первого аргумента, он конструирует Date. Таким образом типы этих инстанций не одинаковы. И соответственно в первом случае display мы вызываем на DateTime и получаем 00:00:00 PM, а во втором случае вывод display из базового класса.
Так вот разница между миллениум_с и миллениум_s в том, что если мы исполним вот так:

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