На предыдущем занятии мы познакомились с классами и объектами и узнали, что класс — это шаблон, по которому создаются объекты.
Также мы выяснили, что класс может содержать:
- атрибуты (данные) — свойства объекта
- методы (функции) — действия объекта
Именно методы позволяют объектам что-то делать.
В этом уроке мы подробно разберём:
- что такое методы класса
- как они вызываются
- зачем нужен параметр
self - как методы работают с конкретным объектом
Это одна из ключевых тем ООП. Понимание self определяет, насколько легко вы будете писать объектно-ориентированный код.
Метод — это функция, объявленная внутри класса.
Он описывает действие, которое может выполнять объект.
Принято придерживаться простого правила именования:
| Тип | Примеры |
|---|---|
| Атрибуты (данные) | color, size, x, y |
| Методы (действия) | set_value, get_param, start, stop |
Другими словами:
данные — существительные методы — глаголы
Это не строгое правило Python, но оно делает код гораздо понятнее.
Создадим простой класс Point.
class Point:
color = "red"
circle = 2
def set_coords(self):
print("Вызов метода set_coords")Разберём код.
Объявление класса:
class Point:
Атрибуты класса:
color = "red"
circle = 2
Метод класса:
def set_coords(self):
На первый взгляд он похож на обычную функцию, но есть одна особенность — параметр self.
Но пока мы не собираемся передавать в нашу функцию аргументы. Давайте уберем этот параметр.
def set_coords():
Попробуем вызвать метод напрямую через класс.
Point.set_coords()Результат:
Вызов метода set_coords
Метод успешно выполнен.
Механизм вызова очень простой:
ИмяКласса.имя_метода()
Важно помнить:
метод — это обычная функция, которая хранится внутри класса.
Теперь создадим объект класса.
pt = Point()Теперь переменная pt хранит экземпляр класса.
Схематично это выглядит так:
Класс Point
│
├── color
├── circle
└── set_coords()
↓
Экземпляр pt
Объект может обращаться к атрибутам и методам класса.
Попробуем получить метод через объект.
pt.set_coordsPython вернёт ссылку на функцию.
Но если попытаться вызвать метод:
pt.set_coords()возникнет ошибка.
TypeError: set_coords() takes 0 positional arguments but 1 was given
Почему так произошло?
Когда метод вызывается через экземпляр класса, Python автоматически передаёт ссылку на объект.
То есть фактически происходит следующее.
Мы пишем:
pt.set_coords()
А Python выполняет:
Point.set_coords(pt)
То есть объект передаётся первым аргументом.
Именно поэтому в методе должен быть параметр self.
self — это ссылка на объект, который вызвал метод.
Исправим наш метод.
class Point:
color = "red"
circle = 2
def set_coords(self):
print("Вызов метода set_coords", self)Теперь вызов:
pt = Point()
pt.set_coords()выведет примерно следующее:
Вызов метода set_coords <__main__.Point object at 0x000001>
То есть self — это сам объект pt.
Когда вызывается метод:
pt.set_coords()
происходит следующее:
pt.set_coords()
↓
Point.set_coords(pt)
И внутри метода:
self = pt
Теперь попробуем снова вызвать метод через класс.
Point.set_coords()Получим ошибку:
TypeError: missing 1 required positional argument: 'self'
Потому что Python не знает, какой объект передать.
Но мы можем передать его вручную.
Point.set_coords(pt)И метод снова выполнится.
Это тот же самый вызов, который Python делает автоматически.
Теперь главный вопрос.
Почему Python использует такую систему?
Ответ очень простой.
Методы класса не копируются в каждый объект.
Они существуют в одном экземпляре внутри класса.
Класс Point
│
└── метод set_coords()
Объекты
├── pt
├── pt2
└── pt3
Все объекты используют один и тот же метод.
Но благодаря self метод знает:
с каким объектом он сейчас работает.
Теперь создадим настоящий полезный метод.
class Point:
color = "red"
circle = 2
def set_coords(self, x, y):
self.x = x
self.y = yРазберём код.
Метод принимает координаты:
def set_coords(self, x, y)
Создаётся атрибут объекта x:
self.x = x
Создаётся атрибут объекта y.
self.y = y
pt = Point()
pt.set_coords(1, 2)Теперь посмотрим внутренние данные объекта.
print(pt.__dict__)Результат:
{'x': 1, 'y': 2}
Это пространство имён объекта.
Каждый объект имеет словарь атрибутов.
pt.__dict__
Схема:
pt
{
"x": 1,
"y": 2
}
Атрибуты создаются в момент присвоения.
То есть строка
self.x = x
фактически делает следующее:
pt.__dict__["x"] = 1
Создадим второй объект.
pt2 = Point()
pt2.set_coords(10, 20)Посмотрим данные:
print(pt.__dict__)
print(pt2.__dict__)Результат:
{'x': 1, 'y': 2}
{'x': 10, 'y': 20}
Объекты не влияют друг на друга.
Именно благодаря self метод знает, в какой объект записывать данные.
Добавим ещё один метод.
class Point:
def set_coords(self, x, y):
self.x = x
self.y = y
def get_coords(self):
return (self.x, self.y)Использование:
pt = Point()
pt.set_coords(3, 5)
print(pt.get_coords())Результат:
(3, 5)
Метод использует данные объекта через self.
Частая ошибка — забыть self.
Например:
def set_coords(x, y):
self.x = xОшибка:
NameError: name 'self' is not defined
Почему?
Потому что self не создаётся автоматически.
Это обычный параметр функции, который мы обязаны объявить.
Правильный вариант:
def set_coords(self, x, y):Интересный факт:
методы — это обычные атрибуты класса, которые хранят функции.
Можно получить метод через getattr.
pt = Point()
method = getattr(pt, "get_coords")
print(method)Результат:
<bound method Point.get_coords of <Point object>>
Теперь можно вызвать его.
print(method())Но на практике почти всегда используется синтаксис:
pt.get_coords()
Он проще и читается лучше.
В этом уроке мы разобрали фундаментальную часть ООП.
Методы — это функции внутри класса, которые описывают поведение объектов.
Главная особенность методов — параметр self.
Он:
- содержит ссылку на текущий объект
- позволяет работать с его данными
- обеспечивает независимость объектов
Без self методы не смогли бы понимать, с каким объектом они работают.
- Что такое метод класса?
- Чем методы отличаются от обычных функций?
- Почему методы обычно называют глаголами?
- Что происходит при вызове метода через объект?
- Что хранится в параметре
self? - Почему при вызове метода через класс возникает ошибка без
self? - Почему методы не копируются в каждый объект?
- Что хранится в
__dict__объекта? - Почему объекты одного класса могут иметь разные значения атрибутов?
- Почему
selfнужно объявлять явно?
Объявите класс MediaPlayer с двумя методами:
-
open(file) — для открытия медиа-файла с именем
file. Метод должен создавать атрибут объектаfilename, содержащий имя файла. -
play() — для воспроизведения файла. Метод должен выводить строку:
Воспроизведение <имя файла>
Создайте два объекта media1 и media2 и вызовите:
media1.open("filemedia1")media2.open("filemedia2")
После этого вызовите метод play() для каждого объекта.
Ожидаемый результат:
Воспроизведение filemedia1
Воспроизведение filemedia2
Объявите класс Lamp, который моделирует лампу.
В классе должны быть два метода:
turn_on() — включает лампу (устанавливает атрибут state = "on")
turn_off() — выключает лампу (устанавливает атрибут state = "off")
Создайте объект lamp.
Последовательно выполните:
lamp.turn_on()
lamp.turn_off()После этого выведите значение атрибута state.
Ожидаемый вывод:
off
Объявите класс Graph со следующими элементами.
Атрибут класса:
LIMIT_Y = [0, 10]
Методы:
-
set_data(data). Принимает список чисел и сохраняет его в атрибуте объекта
data. -
draw(). Выводит числа из списка
data, которые находятся в диапазонеLIMIT_Y.
Границы диапазона включаются.
Числа должны выводиться в одну строку через пробел.
Создайте объект:
graph_1
Передайте ему данные:
[10, -5, 100, 20, 0, 80, 45, 2, 5, 7]
После этого вызовите метод draw().
Ожидаемый вывод:
10 0 2 5 7
Создайте класс Cart, который моделирует корзину покупок.
В классе должны быть методы:
-
add(product) — добавляет товар
productв корзину. -
remove(product) — удаляет товар из корзины, если он там есть.
-
show() — выводит все товары корзины через пробел.
Товары должны храниться в атрибуте объекта items (список).
Создайте объект cart.
Выполните:
cart.add("apple")
cart.add("milk")
cart.add("bread")
cart.remove("milk")
cart.show()
Ожидаемый вывод:
apple bread
Дан класс Translator.
В нём объявлены три метода:
- add(self, eng, rus)
- remove(self, eng)
- translate(self, eng)
В объекте класса должен храниться словарь переводов:
{
"english_word": ["перевод1", "перевод2"]
}
Методы должны работать следующим образом.
-
add(eng, rus)
Добавляет перевод слова.
Если слово уже существует, новый перевод добавляется как синоним.
Если перевод уже есть — добавлять его повторно нельзя.
-
remove(eng)
Удаляет слово из словаря.
-
translate(eng)
Возвращает список переводов.
Создайте объект:
tr
Добавьте переводы:
tree - дерево
car - машина
car - автомобиль
leaf - лист
river - река
go - идти
go - ехать
go - ходить
milk - молоко
Удалите слово:
car
Выведите перевод слова:
go
Ожидаемый вывод:
идти ехать ходить
Имеется класс StreamReader, который считывает строки.
class StreamReader:
FIELDS = ('id', 'title', 'pages')
def readlines(self):
lst_in = input().split()
sd = StreamData()
res = sd.create(self.FIELDS, lst_in)
return sd, resНеобходимо объявить класс StreamData с методом:
create(self, fields, lst_values)
Метод должен:
- Получать кортеж названий полей
fields - Получать список значений
lst_values - Создавать внутри объекта атрибуты с именами из
fields
Например:
fields = ('id', 'title', 'pages')
lst_values = ['1', 'Python', '500']
В объекте должны появиться атрибуты:
id = '1'
title = 'Python'
pages = '500'
Если количество полей и значений не совпадает, метод должен вернуть:
False
И не создавать атрибуты.
Если всё прошло успешно — вернуть:
True