Как вызвать обычный метод класса внутри static метода
Перейти к содержимому

Как вызвать обычный метод класса внутри static метода

Статические методы в 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 , то есть ссылка на конкретный объект, следует задуматься, чтобы сделать метод статическим. Если такой метод необходим только для обеспечения внутренних механизмов работы класса, то возможно его не только надо объявить статическим, но и скрыть от доступа извне.

Особенности статических методов в Python

Пусть у нас будет класс «Цилиндр». При создании объектов от этого класса у них заводятся поля высота и диаметр, а также площадь поверхности. Вычисление площади можно поместить в отдельную статическую функцию. Она вроде и относится к цилиндрам, но с другой стороны само вычисление объекта не требует и может быть использовано где угодно.

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

Статические методы

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

  • Они могут непосредственно вызывать только другие статические методы.
  • Им непосредственно доступны только статические переменные.
  • Они не могут делать ссылки типа this или super .

Пример использования статических методов:

public class StaticMethodClass < static int staticVar = 3; int nonStaticVar; public void nonStaticMethod() < System.out.println("Нестатический метод"); >static void staticMethod(int localVar) < System.out.println("localVar = " + localVar); System.out.println("staticVar = " + staticVar); //Нельзя обратиться к нестатической переменной из статического метода //System.out.println("nonStaticVar language-java">public class StaticMethodDemo < public static void main(String[] args) < StaticMethodClass.staticMethod(42); >>
  • Статические переменные
  • Статический блок
  • Java static import
  • Задания

Trustpilot
Комментарии

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

Статические свойства и методы

Мы также можем присвоить метод самому классу. Такие методы называются статическими.

В объявление класса они добавляются с помощью ключевого слова static , например:

class User < static staticMethod() < alert(this === User); >> User.staticMethod(); // true

Это фактически то же самое, что присвоить метод напрямую как свойство функции:

class User < >User.staticMethod = function() < alert(this === User); >;

Значением this при вызове User.staticMethod() является сам конструктор класса User (правило «объект до точки»).

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

Звучит не очень понятно? Сейчас все встанет на свои места.

Например, есть объекты статей Article , и нужна функция для их сравнения.

Естественное решение – сделать для этого статический метод Article.compare :

class Article < constructor(title, date) < this.title = title; this.date = date; >static compare(articleA, articleB) < return articleA.date - articleB.date; >> // использование let articles = [ new Article("HTML", new Date(2019, 1, 1)), new Article("CSS", new Date(2019, 0, 1)), new Article("JavaScript", new Date(2019, 11, 1)) ]; articles.sort(Article.compare); alert( articles[0].title ); // CSS

Здесь метод Article.compare стоит «над» статьями, как средство для их сравнения. Это метод не отдельной статьи, а всего класса.

Другим примером может быть так называемый «фабричный» метод.

Скажем, нам нужно несколько способов создания статьи:

  1. Создание через заданные параметры ( title , date и т. д.).
  2. Создание пустой статьи с сегодняшней датой.
  3. …или как-то ещё.

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

Такой как Article.createTodays() в следующем примере:

class Article < constructor(title, date) < this.title = title; this.date = date; >static createTodays() < // помним, что this = Article return new this("Сегодняшний дайджест", new Date()); >> let article = Article.createTodays(); alert( article.title ); // Сегодняшний дайджест

Теперь каждый раз, когда нам нужно создать сегодняшний дайджест, нужно вызывать Article.createTodays() . Ещё раз, это не метод одной статьи, а метод всего класса.

Статические методы также используются в классах, относящихся к базам данных, для поиска/сохранения/удаления вхождений в базу данных, например:

// предположим, что Article - это специальный класс для управления статьями // статический метод для удаления статьи по id: Article.remove();

Статические методы недоступны для отдельных объектов

Статические методы могут вызываться для классов, но не для отдельных объектов.

Например. такой код не будет работать:

// . article.createTodays(); /// Error: article.createTodays is not a function

Статические свойства

Новая возможность
Эта возможность была добавлена в язык недавно. Примеры работают в последнем Chrome.

Статические свойства также возможны, они выглядят как свойства класса, но с static в начале:

class Article < static publisher = "Илья Кантор"; >alert( Article.publisher ); // Илья Кантор

Это то же самое, что и прямое присваивание Article :

Article.publisher = "Илья Кантор";

Наследование статических свойств и методов

Статические свойства и методы наследуются.

Например, метод Animal.compare в коде ниже наследуется и доступен как Rabbit.compare :

class Animal < constructor(name, speed) < this.speed = speed; this.name = name; >run(speed = 0) < this.speed += speed; alert(`$бежит со скоростью $.`); > static compare(animalA, animalB) < return animalA.speed - animalB.speed; >> // Наследует от Animal class Rabbit extends Animal < hide() < alert(`$прячется!`); > > let rabbits = [ new Rabbit("Белый кролик", 10), new Rabbit("Чёрный кролик", 5) ]; rabbits.sort(Rabbit.compare); rabbits[0].run(); // Чёрный кролик бежит со скоростью 5.

Мы можем вызвать Rabbit.compare , при этом будет вызван унаследованный Animal.compare .

Как это работает? Снова с использованием прототипов. Как вы уже могли предположить, extends даёт Rabbit ссылку [[Prototype]] на Animal .

Так что Rabbit extends Animal создаёт две ссылки на прототип:

  1. Функция Rabbit прототипно наследует от функции Animal .
  2. Rabbit.prototype прототипно наследует от Animal.prototype .

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

Давайте это проверим кодом:

class Animal <> class Rabbit extends Animal <> // для статики alert(Rabbit.__proto__ === Animal); // true // для обычных методов alert(Rabbit.prototype.__proto__ === Animal.prototype); // true

Итого

Статические методы используются для функциональности, принадлежат классу «в целом», а не относятся к конкретному объекту класса.

Например, метод для сравнения двух статей Article.compare(article1, article2) или фабричный метод Article.createTodays() .

В объявлении класса они помечаются ключевым словом static .

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

class MyClass < static property = . ; static method() < . >>

Технически, статическое объявление – это то же самое, что и присвоение классу:

MyClass.property = . MyClass.method = . 

Статические свойства и методы наследуются.

Для class B extends A прототип класса B указывает на A : B.[[Prototype]] = A . Таким образом, если поле не найдено в B , поиск продолжается в A .

Задачи

Класс расширяет объект?

важность: 3

Как мы уже знаем, все объекты наследуют от Object.prototype и имеют доступ к «общим» методам объекта, например hasOwnProperty .

class Rabbit < constructor(name) < this.name = name; >> let rabbit = new Rabbit("Rab"); // метод hasOwnProperty от Object.prototype alert( rabbit.hasOwnProperty('name') ); // true

Но что если мы явно напишем "class Rabbit extends Object" – тогда результат будет отличаться от обычного "class Rabbit" ?

Ниже пример кода с таким наследованием (почему он не работает? исправьте его):

class Rabbit extends Object < constructor(name) < this.name = name; >> let rabbit = new Rabbit("Кроль"); alert( rabbit.hasOwnProperty('name') ); // Ошибка

Сперва давайте разберёмся, почему код не работает.

Причина становится очевидна, если мы попытаемся запустить его. Унаследованный конструктор класса должен вызывать super() . В противном случае "this" будет не определён.

class Rabbit extends Object < constructor(name) < super(); // надо вызвать конструктор родителя, когда наследуемся this.name = name; >> let rabbit = new Rabbit("Кроль"); alert( rabbit.hasOwnProperty('name') ); // true

Но это ещё не все.

Даже после исправления есть важное различие между "class Rabbit extends Object" и class Rabbit .

Как мы знаем, синтаксис «extends» устанавливает 2 прототипа:

  1. Между "prototype" функций-конструкторов (для методов)
  2. Между самими функциями-конструкторами (для статических методов).

В случае с class Rabbit extends Object это значит:

class Rabbit extends Object <> alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) true

Таким образом, Rabbit предоставляет доступ к статическим методам Object через Rabbit , например:

class Rabbit extends Object <> // обычно мы вызываем Object.getOwnPropertyNames alert( Rabbit.getOwnPropertyNames() ); // a,b

Но если явно не наследуем от объекта, то для Rabbit.__proto__ не установлено значение Object .

class Rabbit <> alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) false (!) alert( Rabbit.__proto__ === Function.prototype ); // как у каждой функции по умолчанию // ошибка - нет такой функции у Rabbit alert( Rabbit.getOwnPropertyNames() ); // Ошибка

Таким образом, в этом случае у Rabbit нет доступа к статическим методам Object .

Кстати, у Function.prototype также есть «общие» методы, такие как call , bind и т. д. Они в конечном итоге доступны в обоих случаях, потому что для встроенного конструктора Object Object.__proto__ === Function.prototype .

Пример на картинке:

Короче говоря, есть два отличия:

class Rabbit class Rabbit extends Object
необходимо вызвать super() в конструкторе
Rabbit.__proto__ === Function.prototype Rabbit.__proto__ === Object

Как вызвать статический метод java

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

// Где-то в другом месте программы class SomeName  public static void someMethod  // Для вызова статического метода нужно // указывать имя класса App.greeting(); > > 

Если метод определен в том же классе, из которого и вызывается, то указывать имя класса перед ним не нужно:

class App  public static String greeting()  return "just a string"; > public static void main(String[] args)  // Тот же класс, поэтому имя не обязательно greeting(); > > 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *