Что такое метод в питоне
Перейти к содержимому

Что такое метод в питоне

  • автор:

Python: статические методы, методы класса и экземпляра класса

not eisenheim

Согласно модели данных Python, язык предлагает три вида методов: статические, класса и экземпляра класса. Давайте посмотрим, что же происходит за кулисами каждого из видов методов. Понимание принципов их работы поможет в создании красивого и эффективного кода. Начнём с самого простого примера, в котором демонстрируются все три вида методов.

Методы экземпляра класса

Это наиболее часто используемый вид методов. Методы экземпляра класса принимают объект класса как первый аргумент, который принято называть self и который указывает на сам экземпляр. Количество параметров метода не ограничено.

Используя параметр self , мы можем менять состояние объекта и обращаться к другим его методам и параметрам. К тому же, используя атрибут self.__class__ , мы получаем доступ к атрибутам класса и возможности менять состояние самого класса. То есть методы экземпляров класса позволяют менять как состояние определённого объекта, так и класса.

Встроенный пример метода экземпляра — str.upper() :

Методы класса

Методы класса принимают класс в качестве параметра, который принято обозначать как cls . Он указывает на класс ToyClass, а не на объект этого класса. При декларации методов этого вида используется декоратор classmethod .

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

Встроенный пример метода класса — dict.fromkeys() — возвращает новый словарь с переданными элементами в качестве ключей.

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

Статические методы декларируются при помощи декоратора staticmethod . Им не нужен определённый первый аргумент (ни self , ни cls ).

Их можно воспринимать как методы, которые “не знают, к какому классу относятся”.

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

С теорией достаточно. Давайте разберёмся с работой методов, создав объект нашего класса и вызвав поочерёдно каждый из методов: instancemethod, classmethod and staticmethod.

Пример выше подтверждает то, что метод instancemethod имеет доступ к объекту класса ToyClass через аргумент self . Кстати, вызов функции obj.instancemethod() используется лишь для удобства, то есть можно использовать и ToyClass.instancemethod(obj) .

Теперь давайте вызовем метод класса:

Мы видим, что метод класса classmethod() имеет доступ к самому классу ToyClass, но не к его конкретному экземпляру объекта. Запомните, в Python всё является объектом. Класс тоже объект, который мы можем передать функции в качестве аргумента.

Заметьте, что self и cls — не обязательные названия и эти параметры можно называть иначе.

Это лишь общепринятые обозначения, которым следуют все. Тем не менее они должны находиться первыми в списке параметров.

Вызовем статический метод:

Да, это может вас удивить, но статические методы можно вызывать через объект класса. Вызов через точку нужен лишь для удобства. На самом же деле в случае статического метода никакие аргументы ( self или cls ) методу не передаются.

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

Теперь давайте вызовем те же самые методы, но на самом классе.

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

Теперь, когда вы знаете разницу между тремя видами методов, давайте рассмотрим реальный пример для понимания того, когда и какой метод стоит использовать. Пример взят отсюда.

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

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

Чаще всего метод класса используется тогда, когда нужен генерирующий метод, возвращающий объект класса. Как видим, метод класса from_birth_year используется для создания объекта класса Person по году рождения, а не возрасту.

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

13. Методы¶

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

  1. Программы строятся из определений объектов и определений функций, и большинство вычислений выражается в терминах операций над объектами.
  2. Каждое определение объекта соответствует некоторому объекту или понятию реального мира, а функции, работающие с объектом, соответствуют тому, как взаимодействуют реальные объекты.

Например, класс Time , определенный в предыдущей главе, соответствует тому, как люди привыкли записывать время. А определенные нами функции соответствуют действиям, которые люди выполняют над временем. Подобным же образом, классы Point и Rectangle соответствуют математическим понятиям точки и прямоугольника.

До сих пор мы не пользовались средствами, которые Python предоставляет для объектно-ориентированного программирования. Строго говоря, эти средства не являются необходимыми для создания программ. По большей части, они предоставляют альтернативный синтаксис для тех вещей, которые мы уже делали. Однако, часто этот синтаксис оказывается более кратким и выразительным.

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

Это наблюдение мотивирует нас перейти к методам. Мы уже встречали некоторые методы, например, keys и values , которые вызываются на словарных объектах. Каждый метод связан с классом и предназначен для использования с объектами этого класса.

Методы похожи на функции, но есть два отличия:

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

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

13.2. Метод increment

Для начала давайте превратим в метод функцию increment .

Для этого достаточно поместить определение функции внутрь определения класса. Хорошим тоном будет также переименование параметра time в self (хоть делать это и не обязательно). Как вы, должно быть, помните, в сообществе программистов Python существует соглашение, согласно которому первому параметру метода дают имя self (англ.: сам).

Обратите внимание на сдвиг кода метода относительно заголовка класса:

Преобразование чисто механическое — мы переместили определение метода в определение класса, и изменили имя первого параметра.

Теперь можно вызвать increment как метод, используя точечную нотацию:

Объект, на котором вызывается метод, присваивается первому параметру метода. Таким образом, в данном случае my_time присваивается параметру self . Второй параметр, seconds , получает значение 600 .

В процедурном программировании предполагается, что функции выполняют необходимые действия. Синтаксис для вызова функции , increment(my_time, 600) , говорит: Эй, increment ! Вот тебе объект Time и 600 секунд, сделай с ними все необходимое.

Синтаксис объектно-ориентированном программировании предполагает, что необходимые действия выполняет объект. Вызов, подобный my_time.increment(600) говорит: Эй, my_time ! Пожалуйста, увеличь себя на 600 секунд!

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

13.3. Более сложный пример¶

Функция after немного более сложная, поскольку она имеет дело с двумя объектами Time . Первый из параметров переименуем в self , второй оставим без изменений:

Мы вызываем этот метод на одном объекте Time , и передаем второй объект Time в качестве аргумента:

Программа состоит из предложений на почти естественном английском языке: Если время готовности (done_time) позднее, чем текущее время (current_time), то.

13.4. Инициализирующий метод¶

Как мы уже знаем, инициализирующий метод — это специальный метод, который вызывается при создании объекта. Этот метод имеет имя __init__ (два символа подчеркивания, init , и еще два символа подчеркивания). Инициализирующий метод класса Time выглядит так:

Заметьте, что между атрибутом self.hours и параметром hours не возникает конфликта имен. Точечная нотация устраняет конфликт.

Метод, вызываемый при создании объекта и инициализирующий состояние объекта, также называют конструктором. В Python метод __init__ является конструктором.

Когда мы создаем объект Time , указанные нами аргументы передаются конструктору:

Поскольку параметры метода __init__ имеют значения по умолчанию, мы можем и не передавать аргументы при создании объекта:

Или передать только первый аргумент:

Или только первые два аргумента:

Мы также можем передать часть аргументов, явно поименовав их:

13.5. Метод __str__

Метод __str__ имеет специальное назначение в Python, он возвращает строковое представление объекта. Определим метод __str__ для класса Time , позаимствовав решение из функции print_time из предыдущей главы:

Если класс предоставляет метод с именем __str__ , то тем самым переопределяет поведение встроенной функции Python str .

При выводе объекта Time с помощью print неявно вызывается __str__ на этом объекте. Поэтому добавление метода __str__ также меняет поведение print :

Как видите, добавление метода __str__ к классу Time сделало ненужным написанную ранее функцию print_time .

Когда мы пишем новый класс, мы почти всегда начинаем с написания метода __init__ , который облегчает создание объектов, и метода __str__ , который часто полезен для отладки.

13.6. Снова Points

Теперь, для закрепления изученного материала, давайте перепишем класс Point в стиле ООП:

Инициализирующий метод принимает x и y как опциональные параметры, значение по умолчанию для каждого из них 0.

Метод __str__ возвращает строковое представление объекта Point :

13.7. Перегрузка операторов¶

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

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

Как обычно, первый параметр метода представляет объект, на котором вызывается метод. Второй параметр удачно назван other (англ.: другой) чтобы противопоставить его первому, self . Для того, чтобы сложить два объекта Point , мы создаем и возвращаем новый объект Point , содержащий сумму координат x и сумму координат y двух объектов.

Теперь, когда мы применяем оператор + к объектам Point , Python вызывает метод __add__ :

Выражение p1 + p2 равнозначно выражению p1.__add__(p2) , только более изящно.

В качестве упражнения вам будет предложено самостоятельно написать метод __sub__(self, other) , который перегрузит оператор вычитания.

Перегрузить оператор умножения можно, определив метод __mul__ , или __rmul__ , или оба эти метода. Если левый операнд оператора * является объектом Point , то Python вызовет метод __mul__ , который ожидает, что второй операнд также является объектом Point . Этот метод рассчитает произведение точек согласно известной из математики формуле (сумма квадратов катетов равна квадрату гипотенузы):

Если левый операнд оператора * является примитивным числовым типом, а правый операнд — объект Point , то Python вызовет метод __rmul__ , который выполнит умножение объекта Point на число:

Результатом будет новый объект Point , чьи координаты кратны первоначальным координатам. Если other окажется типом, который нельзя умножить на число с плавающей точкой, то __rmul__ сгенерирует ошибку.

Следующий пример демонстрирует оба вида умножения:

А что случится, если мы попробуем вычислить p2 * 2 ? Так как первый аргумент является объектом Point , то Python вызовет __mul__ и передаст 2 в качестве второго аргумента. Внутри метода, __mul__ попытается получить атрибут x объекта other , что закончится неудачей, поскольку целое число не имеет атрибутов:

13.8. Полиморфизм¶

Большинство написанных нами методов работают только с определенными типами данных. Когда создается новый класс, то пишутся методы, которые работают с объектами этого класса.

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

Например, операция multadd (обычная в линейной алгебре) имеет три параметра; первые два перемножаются, и к полученному произведению прибавляется третий. Можем записать это на языке Python таким образом:

Этот метод будет работать с любыми значениями x и y , которые можно перемножить, и с любым значением z , которое можно прибавить к полученному произведению.

Можно вызвать этот метод с числовыми значениями:

Или с объектами Point :

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

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

В качестве еще одного примера, рассмотрим метод front_and_back , который печатает список дважды, сначала — в прямом, а затем и в обратном порядке:

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

Вот пример использования метода front_and_back со списком:

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

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

Операции внутри функции включают copy , reverse и print .

copy работает с любым объектом. Мы уже написали метод __str__ для Point . Нам остается написать метод reverse для класса Point :

Теперь можно передать объект Point функции front_and_back :

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

13.9. Глоссарий¶

13.10. Упражнения¶

Переделайте функцию convert_to_seconds в метод класса Time :

Добавьте в класс Point метод __sub__(self, other) , который перегрузит оператор вычитания, и попробуйте с ним поработать.

Перепишите класс Rectangle в объектно-ориентированном стиле, определив методы __init__ и __str__ .

Добавьте в класс Rectangle методы move_rect и grow_rect , созданные на основе одноименных функций из предыдущей главы.

Определите класс Pet (англ.: любимое животное) с атрибутами имя и возраст, инициализируемыми в методе __init__ значениями параметров метода. Метод __str__ должен возвращать строку с именем и возрастом животного. Поэкспериментируйте, создавая объекты класса Pet и выводя их на печать.

Что такое методы в Python

Ваня — ученик 4А класса. Ваня очень любит печеньки.

У строк есть методы (см. методы строк). Воспользуемся методом .replace() :

Код

Игорь — ученик 4А класса. Игорь очень любит печеньки.

В строке про Ваню его имя заменилось на “Игорь”.

Давайте разбираться

Методы похожи на функции. Они тоже пишутся со скобочками и в них тоже передаются аргументы. Отличаются они тем, что привязаны к объекту, для которого вызываются. В примере выше .replace() привязан к строке "Ваня — ученик 4А класса. Ваня очень любит печеньки." . Он заменил в этой строке все кусочки, где написано “Ваня” на “Игорь”.

Методы придумали для того, чтобы привязывать функции к типу данных. Вы уже работали со строками и числами. Забегая вперёд, есть и другие типы данных: списки, множества, даже картинки! Функция замены кусочков строки на другие нужна только для строк, с числами такое не делают. Функция округления нужна только для чисел, строку нельзя “округлить”. Чтобы не гадать какая функция для каких типов данных — придумали методы, привязанные к этим типам точкой.

Если всё ещё не понятно:

Попробуйте бесплатные уроки по Python

Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.

Методы в Python

Здесь вы можете прочитать про обычные методы. Также такие методы называют instance methods.

Методы это функции внутри классов. Аргументы, которые принимет метод — это объект класса, какие-то другие нужные для этого метода аргументы.

Если говорить простыми словами — «с улицы» метод вызвать нельзя, сначала нужно сообщить хотя бы с каким классом нужно работать.

В случае обычных методов нужно ещё и передать объект класса.

Отличие метода от функции

Под методом подразумевается обычный метод, не статический и не classmethod Метод это функция в рамках какого-то класса.

Метод ждёт, что его применят к объекту его класса

В метод по-умолчанию нужно передавать объект его класса.

Функция ждёт что в неё передадут определённые аргументы либо работает вообще без аргументов

Аргументы могут быть как стандартными объектами так и объектами пользовательского класса. Это определятеся при объявлении функции

В функцию у которой нет параметров можно вообще ничего не передавать и она будет работать.

Чтобы отличать обычные функции от методов можно использовать термин свободные фукнции (free functions)

Свободные функции определены в глобальной области видимости или в области видимости модуля. Методы определены внутри классов.

Пример

Напишем метод для класса Site, который будет просто выводить на экран информацию об объекте.

class Site (): def describe (self): print (f "Website is available at and was created in " ) pass hh = Site() hh.url = "https://www.heihei.ru" hh.name = "HeiHei.ru" hh.established = 2018 hh.describe()

Website HeiHei.ru is available at https://www.heihei.ru and was created in 2018

Метод работает, но если, например, забыть задать один из атрибутов — объект по-прежнему создаётся, но метод уже не работает

Traceback (most recent call last): File «/home/andrei/python/method_example.py», line 12, in <module> hh.describe() File «/home/andrei/python/method_example.py», line 3, in describe print(f»Website is available at and was created in «) AttributeError: 'Site' object has no attribute 'established'

Чтобы объекты создавались сразу со всеми необходимыми атрибутами, а также для сокращения количества кода и возможности сходу комбинировать разные атрибуты используют метод __init__()

__init__()

Метод __init__() позволяет быстрее создавать объекты данного класса.

__init__() это не конструктор (с которым вы могли столкнуться, например в Java ) а инициализатор.

То есть объет прекрасно создаётся и без __init__(), который нужен для инициализации атрибутов.

Сравните два класса Site без __init__() и WebSite с __init__()

class Site (): pass hh = Site() hh.url = "https://www.heihei.ru" hh.name = "HeiHei.ru" hh.description = "Сайт о праздниках" hh.established = 2018 tb = Site() tb.url = "https://www.topbicycle.ru" tb.name = "TobBicycle" tb.description = "Обзоры на велосипеды" tb.established = 2018 print (hh.url, hh.name, hh.description, hh.established) print (tb.url, tb.name, tb.description, tb.description) class WebSite (): def __init__ (self, url, name, description, established): self.url = url self.name = name self.description = description self.established = established hh = WebSite( "https://www.heihei.ru" , "HeiHei.ru" , "Сайт о праздниках" , 2018 ) tb = WebSite( "https://www.topbicycle.ru" , "TobBicycle.ru" , "Обзоры на велосипеды" , 2018 ) print (hh.url, hh.name, hh.description, hh.established) print (tb.url, tb.name, tb.description, tb.description)

Также метод __init__() позволяет создавать дополнительные атрибуты из уже заданных

Рассмотрим класс Employee, и метод __init__() который будет принимать всего два аргумента имя (first) и фамилия (last)

class Employee : def __init__ (self, first, last): self.first = first self.last = last self.fullname = first + ' ' + last self.email = first + '.' + last + '@vostok.su' emp_1 = Employee( 'Yuri' , 'Gagarin' ) emp_2 = Employee( 'Test' , 'User' ) print (f " email is " ) print (f " email is " )

Yuri Gagarin email is Yuri.Gagarin@vostok.su Test User email is Test.User@vostok.su

С помощью метода __init__() мы без лишнего труда создали атрибуты полное имя (fullname) и электронная почта (email)

Значения по умолчанию

С помощью __init__() можно указать значения, которые получат атрибуты, в случае, если они не заданы при создании объекта.

В следующем примере, можно создавать объекты класса Cosmonaut как указывая, так и не указывая имя космонавта. Достаточно одной фамилии.

class Cosmonaut : def __init__ (self, last, first = "Сosmonaut" ): self.last = last self.first = first self.fullname = first + ' ' + last self.email = first + '.' + last + '@vostok.su' cosm_1 = Cosmonaut( 'Gagarin' ) cosm_2 = Cosmonaut( 'Alexei' , 'Leonov' ) print (f " email is " ) print (f " email is " )

Сosmonaut Gagarin email is Сosmonaut.Gagarin@vostok.su Leonov Alexei email is Leonov.Alexei@vostok.su

Резюмируем преимущества, которые даёт нам __init__()

  • Меньше кода
  • Можно задавать значения по умолчанию → все обязательные атрибуты будут на месте
  • Возможность комбинировать атрибуты в новые прямо во время создания объекта

Ещё несколько слов про self

Рассмотрим скрипт method_example.py

class Dog (): bioclass = "mammal" def __init__ (self, breed, name, spots): self.breed = breed self.name = name self.spots = spots def bark (self): print ( "WOOF!" ) my_dog = Dog( "husky" , "Barbos" , True ) my_dog.bark()

Что будет если убрать из

def bark (self):

Traceback (most recent call last): File "/home/andrei/python/method_example.py", line 14, in <module> my_dog.bark() TypeError: bark() takes 0 positional arguments but 1 was given

Казалось бы никаких аргументов в bark() мы не передавали. Тем не менее один передался автоматически. А всё потому, что запись

Это краткая версия

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

def bark (self):

self похож на this в таких языках как C++ , Java и C#

Изображение баннера

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

Рассмотрим список (list) в интерактивном режиме

>>> mylist = [1,2,3] >>> mylist.append(4) >>> mylist [1, 2, 3, 4] >>> mylist.pop() 4 >>> mylist [1, 2, 3] >>> help(mylist.insert) Help on built-in function insert: insert(index, object, /) method of builtins.list instance Insert object before index.

Изображение баннера

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

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

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