Преобразует метод класса в статический метод этого класса
Функция — декоратор staticmethod() преобразует метод класса в статический метод этого класса. Статический метод не получает неявный первый аргумент self класса.
Чтобы объявить статический метод, используйте эту идиому:
class C: @staticmethod def f(arg1, arg2, . ): .
Проще говоря, декоратор @staticmethod превращает метод, определенный внутри класса, в обычную функцию, которая не имеет доступа к экземпляру класса self , поэтому ее можно вызывать без создания экземпляра класса. Следовательно статический метод может быть вызван либо в классе C.f() , либо в экземпляре класса C().f() .
Статические методы делают код более читабельным и повторно используемым поскольку не знают о состоянии текущего экземпляра класса. Также статические методы более удобны по сравнению с обычными функциями в отношении их импорта, так как нет необходимости импортировать каждую функцию отдельно. Выполняется импорт только одного класса с набором определенных в нем статических методов.
Статические методы в Python похожи на те, которые есть в Java или C++. Еще посмотрите вариант classmethod() , который полезен для создания конструкторов альтернативных классов.
Как и все декораторы, staticmethod() можно вызывать как обычную функцию и что-то делать с ее результатом. Это необходимо в некоторых случаях, когда вам нужна ссылка на функцию из тела класса, и вы хотите избежать автоматического преобразования в метод экземпляра. Для этих случаев используйте эту идиому:
class C: builtin_open = staticmethod(open)
Изменено в Python 3.10: статические методы теперь наследуют атрибуты метода ( __module__ , __name__ , __qualname__ , __doc__ и __annotations__ ), имеют новый атрибут __wrapped__ и теперь могут быть вызваны как обычные функции.
- ОБЗОРНАЯ СТРАНИЦА РАЗДЕЛА
- Функция abs(), абсолютное значение числа
- Функция all(), все элементы True
- Функция any(), хотя бы один элемент True
- Функция ascii(), преобразует строку в ASCII
- Функция bin(), число в двоичную строку
- Класс bool(), логическое значение объекта
- Функция breakpoint(), отладчик кода
- Класс bytearray(), преобразует в массив байтов
- Класс bytes(), преобразует в строку байтов
- Функция callable(), проверяет можно ли вызвать объект
- Функция chr(), число в символ Юникода
- Класс classmethod, делает функцию методом класса
- Функция compile() компилирует блок кода Python
- Класс complex(), преобразует в комплексное число
- Функция delattr(), удаляет атрибут объекта
- Класс dict() создает словарь
- Функция dir(), все атрибуты объекта
- Функция divmod(), делит числа с остатком
- Функция enumerate(), счетчик элементов последовательности
- Функция eval(), выполняет строку-выражение с кодом
- Функция exec(), выполняет блок кода
- Функция filter(), фильтрует список по условию
- Класс float(), преобразует в вещественное число
- Функция format(), форматирует значение переменной
- Класс frozenset(), преобразует в неизменяемое множество
- Функция getattr(), значение атрибута по имени
- Функция globals(), переменные глобальной области
- Функция hasattr(), наличие атрибута объекта
- Функция hash(), хэш-значение объекта
- Функция help(), справка по любому объекту
- Функция hex(), число в шестнадцатеричную строку
- Функция id(), идентификатор объекта
- Функция input(), ввод данных с клавиатуры
- Класс int(), преобразует в тип int
- Функция isinstance(), принадлежность экземпляра к классу
- Функция issubclass(), проверяет наследование класса
- Функция iter(), создает итератор
- Функция len(), количество элементов объекта
- Класс list(), преобразовывает в список
- Функция locals(), переменные локальной области
- Функция map(), обработка последовательности без цикла
- Функция max(), максимальное значение элемента
- Класс memoryview(), ссылка на буфер обмена
- Функция min(), минимальное значение элемента
- Функция next(), следующий элемент итератора
- Класс object(), возвращает безликий объект
- Функция oct(), число в восьмеричную строку
- Функция open(), открывает файл на чтение/запись
- Функция ord(), число символа Unicode
- Функция pow(), возводит число в степень
- Функция print(), печатает объект
- Класс property(), метод класса как свойство
- Класс range(), генерирует арифметические последовательности
- Функция repr(), описание объекта
- Функция reversed(), разворачивает последовательность
- Функция round(), округляет число
- Класс set(), создает или преобразовывает в множество
- Функция setattr(), создает атрибут объекта
- Класс slice(), шаблон среза
- Функция sorted(), выполняет сортировку
- Декоратор staticmethod(), метод класса в статический метод
- Класс str(), преобразует объект в строку
- Функция sum(), сумма последовательности
- Функция super(), доступ к унаследованным методам
- Класс tuple(), создает или преобразует в кортеж
- Класс type(), возвращает тип объекта
- Функция vars(), словарь переменных объекта
- Функция zip(), объединить элементы в список кортежей
- Функция __import__(), находит и импортирует модуль
- Функция aiter(), создает асинхронный итератор
- Функция anext(), следующий элемент асинхронного итератора
Статические методы в Python
Ранее было сказано, что классы можно рассматривать как модули, содержащие переменные со значениями (в данном случае речь идет о тех, что находятся вне методов) и функции. Только здесь переменные называются полями или свойствами класса, а функции – методами класса. Вместе поля и методы мы называем атрибутами.
Однако в случае классов, когда метод применяется к объекту, этот экземпляр передается в метод в качестве первого аргумента:
>>> class A: . def meth(self): . print('meth') . >>> a = A() >>> a.meth() meth >>> A.meth(a) meth
Вызов a.meth() на самом деле преобразуется к A.meth(a) , то есть мы идем к «модулю A» и в его пространстве имен ищем атрибут meth . Там оказывается, что meth это функция, принимающая один обязательный аргумент. Тогда ничего не мешает сделать так:
>>> b = 10 >>> A.meth(b) meth
В таком «модульном формате» вызова методов передавать объект-экземпляр именно класса A совсем не обязательно. Однако нельзя сделать так:
>>> b = 10 >>> b.meth() Traceback (most recent call last): File "", line 1, in AttributeError: 'int' object has no attribute 'meth'
Если объект передается методу в нотации через точку, то этот метод должен быть описан в том классе, которому принадлежит объект, или в родительских классах. В данном случае у класса int нет метода meth . Объект b классу A не принадлежит. Поэтому интерпретатор никогда не найдет метод meth .
Что делать, если возникает необходимость в методе, который бы не принимал объект данного класса в качестве аргумента? Да, мы можем объявить метод вообще без параметров и вызывать его только через класс:
>>> class A: . def meth(): . print('meth') . >>> A.meth() meth >>> a = A() >>> a.meth() Traceback (most recent call last): File "", line 1, in TypeError: meth() takes 0 positional arguments but 1 was given
Получается странная ситуация. Ведь meth можно вызывать не только через класс, но и через порожденные от него объекты (в ошибке выше говорится о несовпадении количества аргументов, а не об отсутствии у экземпляра самого метода). Однако в последнем случае всегда будет выбрасываться исключение. То есть имеется потенциально ошибочный код. Кроме того, может понадобиться метод с параметрами, но которому не надо передавать экземпляр данного класса.
В ряде языков программирования, например в Java, для таких ситуаций предназначены статические методы. При описании этих методов в их заголовке ставится ключевое слово static . Такие методы могут вызываться через объекты данного класса, но сам объект в качестве аргумента в них не передается.
В Python острой необходимости в статических методах нет, так как код может находиться за пределами класса, и программа не начинает выполняться из класса. Если нам нужна просто какая-нибудь функция, то есть такая, которая не подразумевает принятие в качестве аргумента экземпляра класса, мы можем определить ее в основной ветке программы. В Java это не так. Там, не считая импортов, весь код находится внутри классов. Поэтому методы, не принимающие объект данного класса и играющие роль обычных функций, необходимы. И ввод в язык такой синтаксической конструкции как статический метод решают эту проблему.
Однако в Python тоже можно реализовать подобное, то есть статические методы, с помощью декоратора @staticmethod :
>>> class A: . @staticmethod . def meth(): . print('meth') . >>> a = A() >>> a.meth() meth >>> A.meth() meth
Пример с параметром:
>>> class A: . @staticmethod . def meth(value): . print(value) . >>> a = A() >>> a.meth(1) 1 >>> A.meth('hello') hello
Статические методы в Python – по-сути обычные функции, помещенные в класс для удобства и находящиеся в пространстве имен этого класса. Это может быть какой-то вспомогательный код. Вообще, если в теле метода не используется self , то есть ссылка на конкретный объект, следует задуматься, чтобы сделать метод статическим. Если такой метод необходим только для обеспечения внутренних механизмов работы класса, то возможно его не только надо объявить статическим, но и скрыть от доступа извне.
Пусть у нас будет класс «Цилиндр». При создании объектов от этого класса у них заводятся поля высота и диаметр, а также площадь поверхности. Вычисление площади можно поместить в отдельную статическую функцию. Она вроде и относится к цилиндрам, но с другой стороны само вычисление объекта не требует и может быть использовано где угодно.
from math import pi class Cylinder: @staticmethod def make_area(d, h): circle = pi * d ** 2 / 4 side = pi * d * h return round(circle*2 + side, 2) def __init__(self, di, hi): self.dia = diameter self.h = high self.area = self.make_area(di, hi) a = Cylinder(1, 2) print(a.area) print(a.make_area(2, 2))
В примере вызов make_area() за пределами класса возможен в том числе через экземпляр. При этом свойство area самого объекта a не меняется. Мы просто вызываем функцию, находящуюся в пространстве имен класса.
Практическая работа
Приведенный в конце урока пример плохой. Мы можем менять значения полей dia и h объекта за пределами класса простым присваиванием (например, a.dia = 10 ). При этом площадь никак не будет пересчитываться. Также мы можем назначить новое значение для площади, как простым присваиванием, так и вызовом функции make_area() с последующим присваиванием. Например, a.area = a.make_area(2, 3) . При этом не меняются высота и диаметр.
Защитите код от возможных логических ошибок следующим образом:
- Свойствам dia и h объекта по-прежнему можно выполнять присваивание за пределами класса. Однако при этом «за кулисами» происходит пересчет площади, то есть изменение значения area .
- Свойству area нельзя присваивать за пределами класса. Можно только получать его значение.
Подсказка: вспомните про метод __setattr__ .
Курс с примерами решений практических работ:
pdf-версия
X Скрыть Наверх
Объектно-ориентированное программирование на Python
Зачем в питоне нужен @staticmethod
Есть класс printer . У него есть 2 метода, делающие одно и то же. Различие в том, что один из методов — с декоратором @staticmethod , а другой — без. Но я могу вызывать оба метода без создания экземпляра класса.
class printer(): ''' Тест @staticmethod ''' def not_static_print(self, text = 'Example Text'): print(text) @staticmethod def static_print(text = 'Example Text'): print(text) # Не создаю никаких экземпляров printer.not_static_print(None, 'Emm?') printer.static_print('Something like this.')
Просто для not_static_print() я указываю экземпляр, а точнее его отсутствие ( None ) Есть ли принципиальная разница в использовании этих методов?
Отслеживать
149k 12 12 золотых знаков 59 59 серебряных знаков 132 132 бронзовых знака
задан 6 авг 2018 в 17:19
Don2Quixote Don2Quixote
1,705 3 3 золотых знака 12 12 серебряных знаков 26 26 бронзовых знаков
p = printer() p.not_static_print(None, ‘Emm?’) а теперь попробуйте вызвать тот not_static_print с None первым аргументом 🙂
6 авг 2018 в 17:29
кст, в вашем примере у класса нет полей объекта и в not_static_print вы к ним не обращайтесь, а это будет возможно только через self , но если вы это сделайте и передадите None , то будет ошибка AttributeError: ‘NoneType’ object has no attribute . Вот для таких случаев и существуют статичные методы — они не могут работать с полями и методами объекта, но при этом их не сделали просто функциями, т.к. они логически относятся к данному классу / типу
Метод classmethod() и staticmethod в Python
Метод classmethod() возвращает метод класса для данной функции.
classmethod(function)
classmethod() считается не-Pythonic, поэтому в более новых версиях Python вы можете использовать декоратор @classmethod для определения метода класса.
@classmethod def func(cls, args. )
Метод classmethod() принимает единственный параметр:
- function — Функция, которую нужно преобразовать в метод класса.
И возвращает метод класса для данной функции.
Что такое метод класса?
Метод класса — это метод, который привязан к классу, а не к его объекту. Он не требует создания экземпляра класса, как staticmethod.
Разница между статическим методом и методом класса:
- Статический метод ничего не знает о классе и имеет дело только с параметрами.
- Метод класса работает с классом, поскольку его параметром всегда является сам класс.
Метод класса может вызываться как классом, так и его объектом.
Class.classmethod() Or even Class().classmethod()
Но несмотря ни на что, метод класса всегда присоединяется к классу с первым аргументом, поскольку класс cls .
def classMethod(cls, args. )
Пример 1: Создать метод класса с помощью classmethod()
class Person: age = 25 def printAge(cls): print('The age is:', cls.age) # create printAge class method Person.printAge = classmethod(Person.printAge) Person.printAge()
The age is: 25
Здесь у нас есть класс Person с возрастом переменной, присвоенным 25. У нас также есть функция printAge, которая принимает единственный параметр cls, а не self, который мы обычно принимаем. cls принимает в качестве параметра класс Person, а не объект или экземпляр Person.
Теперь мы передаем метод Person.printAge в качестве аргумента методу класса функции. Это преобразует метод в метод класса, так что он принимает первый параметр как класс (т.е. Person).
В последней строке мы вызываем printAge без создания объекта Person, как мы это делаем для статических методов. Это печатает возраст переменной класса.
Когда вы использовать метод класса?
1 Фабричные методы
Фабричные методы — это те методы, которые возвращают объект класса (например, конструктор) для различных вариантов использования.
Это похоже на перегрузку функций в C ++. Поскольку в Python нет ничего как такового, используются методы классов и статические методы.
Пример 2: Создание фабричного метода с использованием метода класса
from datetime import date # random Person class Person: def __init__(self, name, age): self.name = name self.age = age @classmethod def fromBirthYear(cls, name, birthYear): return cls(name, date.today().year - birthYear) def display(self): print(self.name + "'s age is: " + str(self.age)) person = Person('Adam', 19) person.display() person1 = Person.fromBirthYear('John', 1985) person1.display()
Adam's age is: 19 John's age is: 31
Здесь у нас есть два создателя экземпляра класса, конструктор и метод fromBirthYear.
Конструктор принимает обычные параметры name и age. В то время как fromBirthYear принимает класс, имя и год рождения, вычисляет текущий возраст, вычитая его из текущего года, и возвращает экземпляр класса.
Метод fromBirthYear принимает класс Person (а не объект Person) в качестве первого параметра cls и возвращает конструктор путем вызова cls (name, date.today(). Year — BirthYear), что эквивалентно Person (name, date.today() .year — Год рождения)
Перед методом мы видим @classmethod. Это называется декоратором для преобразования fromBirthYear в метод класса как classmethod().
2 Правильное создание экземпляра в наследовании
Всякий раз, когда вы создаете класс из реализации фабричного метода как метода класса, это обеспечивает правильное создание экземпляра производного класса.
Вы можете создать статический метод для приведенного выше примера, но создаваемый им объект всегда будет закодирован как базовый класс.
Но когда вы используете метод класса, он создает правильный экземпляр производного класса.
Пример 3: Как метод класса работает для наследования?
from datetime import date # random Person class Person: def __init__(self, name, age): self.name = name self.age = age @staticmethod def fromFathersAge(name, fatherAge, fatherPersonAgeDiff): return Person(name, date.today().year - fatherAge + fatherPersonAgeDiff) @classmethod def fromBirthYear(cls, name, birthYear): return cls(name, date.today().year - birthYear) def display(self): print(self.name + "'s age is: " + str(self.age)) class Man(Person): sex = 'Male' man = Man.fromBirthYear('John', 1985) print(isinstance(man, Man)) man1 = Man.fromFathersAge('John', 1965, 20) print(isinstance(man1, Man))
True False
Здесь использование статического метода для создания экземпляра класса требует от нас жесткого кодирования типа экземпляра во время создания.
fromFathersAge возвращает не объект Man, а объект его базового класса Person.
Это нарушает парадигму ООП. Использование метода класса от fromBirthYear может гарантировать ООП-кодирование кода, поскольку он принимает первый параметр как сам класс и вызывает его фабричный метод.
Встроенная функция staticmethod() возвращает статический метод для данной функции.
staticmethod(function)
В более новых версиях Python вы можете использовать декоратор @staticmethod.
@staticmethod def func(args, . )
Метод staticmethod() принимает единственный параметр:
- function — функция, которую нужно преобразовать в статический метод
Staticmethod() возвращает статический метод для функции, переданной в качестве параметра.
Что такое статический метод?
Статические методы, как и методы класса, — это методы, привязанные к классу, а не к его объекту. Они не требуют создания экземпляра класса. Они не зависят от состояния объекта.
Разница между статическим методом и методом класса:
- Статический метод ничего не знает о классе и имеет дело только с параметрами.
- Метод класса работает с классом, поскольку его параметром всегда является сам класс.
Их может вызывать как класс, так и его объект.
Class.staticmethodFunc() or even Class().staticmethodFunc()
Создание статического метода с помощью staticmethod()
class Mathematics: def addNumbers(x, y): return x + y # create addNumbers static method Mathematics.addNumbers = staticmethod(Mathematics.addNumbers) print('The sum is:', Mathematics.addNumbers(5, 10))
The sum is: 15
Когда использовать статические методы?
1. Группирование функции в класс
Статические методы имеют ограниченный вариант использования, потому что, как методы класса или любые другие методы внутри класса, они не могут получить доступ к свойствам самого класса.
Однако, когда вам нужна служебная функция, которая не имеет доступа к каким-либо свойствам класса, но имеет смысл, что она принадлежит этому классу, мы используем статические функции.
Создание служебной функции как статического метода
class Dates: def __init__(self, date): self.date = date def getDate(self): return self.date @staticmethod def toDashDate(date): return date.replace("/", "-") date = Dates("15-12-2016") dateFromDB = "15/12/2016" dateWithDash = Dates.toDashDate(dateFromDB) if(date.getDate() == dateWithDash): print("Equal") else: print("Unequal")
Equal
Здесь у нас есть класс Dates, который работает только с датами с тире. Однако в нашей предыдущей базе данных все даты были указаны в виде косой черты.
Чтобы преобразовать даты с косой чертой в даты с тире, мы создали служебную функцию toDashDate в Dates.
Это статический метод, потому что он не требует доступа к каким-либо свойствам самой даты, а требует только параметров.
Мы также можем создать toDashDate вне класса, но поскольку он работает только для дат, логично оставить его внутри класса Dates.
Наличие единой реализации
Статические методы используются, чтобы подклассы класса изменяли или переопределяли конкретную реализацию метода.
Как наследование работает со статическим методом?
class Dates: def __init__(self, date): self.date = date def getDate(self): return self.date @staticmethod def toDashDate(date): return date.replace("/", "-") class DatesWithSlashes(Dates): def getDate(self): return Dates.toDashDate(self.date) date = Dates("15-12-2016") dateFromDB = DatesWithSlashes("15/12/2016") if(date.getDate() == dateFromDB.getDate()): print("Equal") else: print("Unequal")
Equal
Здесь мы не хотели бы, чтобы подкласс DatesWithSlashes переопределял статический служебный метод toDashDate, потому что он используется только один раз, то есть меняет дату на тире-даты.
Мы могли бы легко использовать статический метод в наших интересах, переопределив метод getDate() в подклассе, чтобы он хорошо работал с классом DatesWithSlashes.
