Создание классов и объектов
В языке программирования Python классы создаются с помощью инструкции class, за которой следует произвольное имя класса, после которого ставится двоеточие, далее с новой строки и с отступом реализуется тело класса:
class ИмяКласса: код_тела_класса
Если класс является дочерним, то родительские классы перечисляются в круглых скобках после имени класса.
Объект создается путем вызова класса по его имени. При этом после имени класса обязательно ставятся скобки:
ИмяКласса()
То есть класс вызывается подобно функции. Однако при этом происходит не выполнение его тела, а создается объект. Поскольку в программном коде важно не потерять ссылку на только что созданный объект, то обычно его связывают с переменной. Поэтому создание объекта чаще всего выглядит так:
имя_переменной = ИмяКласса()
В последствии к объекту обращаются через связанную с ним переменную.
Пример "пустого" класса и двух созданных на его основе объектов:
>>> class A: ... pass ... >>> a = A() >>> b = A()
Класс как модуль
В языке программирования Python класс можно представить подобным модулю. Также как в модуле в нем могут быть свои переменные со значениями и функции. Также как в модуле у класса есть собственное пространство имен, доступ к которым возможен через имя класса:
>>> class B: ... n = 5 ... def adder(v): ... return v + B.n ... >>> B.n 5 >>> B.adder(4) 9
Однако в случае классов используется немного иная терминология. Пусть имена, определенные в классе, называются атрибутами этого класса. В примере имена n
и adder
– это атрибуты класса B
. Атрибуты-переменные часто называют полями или свойствами. Свойством является n
. Атрибуты-функции называются методами. Методом в классе B
является adder
. Количество свойств и методов в классе может быть любым.
Класс как создатель объектов
Приведенный выше класс позволяет создавать объекты, но мы не можем применить к объекту метод adder():
>>> l = B() >>> l.n 5 >>> l.adder(100) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: adder() takes 1 positional argument but 2 were given
В сообщении об ошибке говорится, что adder() принимает только один аргумент, а было передано два. Откуда взялся второй аргумент, и кто он такой, если в скобках было указано только одно число 100?
На самом деле классы – это далеко не модули. Они идут дальше модулей и обладают своими особенностями. Класс создает объекты, которые в определенном смысле являются его наследниками. Это значит, что если у объекта нет собственного поля n
, то интерпретатор ищет его уровнем выше, то есть в классе. Таким образом, если мы присваиваем объекту поле с таким же именем как в классе, то оно перекрывает, т. е. переопределяет, поле класса:
>>> l.n = 10 >>> l.n 10 >>> B.n 5
Здесь l.n
и B.n
– это разные переменные. Первая находится в пространстве имен объекта l
. Вторая – в пространстве класса B
. Если бы мы не добавили поле n
к объекту l
, то интерпретатор бы поднялся выше по дереву наследования и пришел бы в класс, где бы и нашел это поле.
Что касается методов, то они также наследуются объектами класса. В данном случае у объекта l
нет своего собственного метода adder, значит, он ищется в классе B
. Однако от класса B
может быть порождено множество объектов. Методы же чаще всего предназначаются для обработки объектов. Таким образом, когда вызывается метод, в него надо передать конкретный объект, который он будет обрабатывать.
Понятно, что передаваемый экземпляр, это объект, к которому применяется метод. Выражение l.adder(100) выполняется интерпретатором следующим образом:
-
Ищу атрибут adder() у объекта
l
. Не нахожу. -
Тогда иду искать в класс
B
, так как он создал объектl
. -
Здесь нахожу искомый метод. Передаю ему объект, к которому этот метод надо применить, и аргумент, указанный в скобках.
Другими словами, выражение l.adder(100)
преобразуется в выражение B.adder(l, 100)
.
Таким образом, интерпретатор попытался передать в метод adder() класса B
два параметра – объект l
и число 100. Но мы запрограммировали метод adder() так, что он принимает только один параметр. В Python, да и многих других языках, определения методов не предполагают принятие объекта как само собой подразумеваемое. Принимаемый объект надо указывать явно.
По соглашению в Python для ссылки на объект используется имя self. Вот так должен выглядеть метод adder(), если мы планируем вызывать его через объекты:
>>> class B: ... n = 5 ... def adder(self, v): ... return v + self.n ...
Переменная self связывается с объектом, к которому был применен данный метод, и через эту переменную мы получаем доступ к атрибутам объекта. Когда этот же метод применяется к другому объекту, то self свяжется уже с этим другим объектом, и через эту переменную будут извлекаться только его свойства.
Протестируем обновленный метод:
>>> l = B() >>> m = B() >>> l.n = 10 >>> l.adder(3) 13 >>> m.adder(4) 9
Здесь от класса B
создаются два объекта – l
и m
. Для объекта l
заводится собственное поле n
. Объект m
, за неимением собственного, наследует n
от класса B
. Можно в этом убедиться, проверив соответствие:
>>> m.n is B.n True >>> l.n is B.n False
В методе adder() выражение self.n
– это обращение к свойству n
, переданного объекта, и не важно, на каком уровне наследования оно будет найдено.
Если метод не принимает объект, к которому применяется, в качестве первого параметра, то такие методы в других языках программирования называются статическими. Они имеют особый синтаксис и могут вызываться как через класс, так и через объект этого класса. В Python все немного по-другому. Для имитации статических методов используется специальный декоратор, после чего метод можно вызывать не только через класс, но и через объект, не передавая сам объект.
Изменение полей объекта
В Python объекту можно не только переопределять поля и методы, унаследованные от класса, также можно добавить новые, которых нет в классе:
>>> l.test = "hi" >>> B.test Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: type object 'B' has no attribute 'test' >>> l.test 'hi'
Однако в программировании так делать не принято, потому что тогда объекты одного класса будут отличаться между собой по набору атрибутов. Это затруднит автоматизацию их обработки, внесет в программу хаос.
Поэтому принято присваивать полям, а также получать их значения, путем вызова методов:
>>> class User: ... def setName(self, n): ... self.name = n ... def getName(self): ... try: ... return self.name ... except: ... print("No name") ... >>> first = User() >>> second = User() >>> first.setName("Bob") >>> first.getName() 'Bob' >>> second.getName() No name
Подобные методы в простонародье называют сеттерами (set – установить) и геттерами (get – получить).
Практическая работа
Напишите программу по следующему описанию. Есть класс "Воин". От него создаются два экземпляра-юнита. Каждому устанавливается здоровье в 100 очков. В случайном порядке они бьют друг друга. Тот, кто бьет, здоровья не теряет. У того, кого бьют, оно уменьшается на 20 очков от одного удара. После каждого удара надо выводить сообщение, какой юнит атаковал, и сколько у противника осталось здоровья. Как только у кого-то заканчивается ресурс здоровья, программа завершается сообщением о том, кто одержал победу.
Курс с примерами решений практических работ и всеми уроками: android-приложение, pdf-версия.
Комментарии
class android1: ""…
Доброго времени суток,…
Доброго времени суток, помогите, что я делаю не так?
хочу передать здоровье мышки в класс змеи, программа исполняется, но ничего не выводит на экран. Спасибо
def set_health(self,…
Ответ на Доброго времени суток,… от Юрий
Здесь забыто self во второй строке. Надо:
Мое решение ООП 2 глава
Решение
Почему and в условии while?
Ответ на Решение от Karman
Вы неправильно рассуждаете…
Ответ на Почему and в условии while? от Sataev_Adilet
Вы неправильно рассуждаете. При or цикл работает до тех пор, пока хотя бы одно условие возвращает true. При or, чтобы цикл остановился, надо чтобы оба условия вернули false.
При and достаточно одного false, чтобы цикл остановился. Цикл работает, только если оба условия возвращают true.
class warrior: def…
import random class Warrior:…
задание
Ответ на import random class Warrior:… от Виталий
Yurii_Ver
Игра
Просто бомбезно!
Answer
result
Ке
Ответ
Практическая работа
Мой вариант решения задания
Слишком много print
Ответ на Мой вариант решения задания от Некто из Екате…
My version
Реншение
Здравствуйте!Вопрос чисто…
Здравствуйте!
Вопрос чисто нубский
Как в пайтон промпте у вас получается переносить строку с отступом без выполнения команды?
>>> class B:
... n = 5
... def adder(self, v):
... return v + self.n
...
Первый отсуп получется автоматически после двоеточия, а вот второй, после n=5 приступает к выполнению и соответственно выдает ошибку.
Работаю в Windows cmd
Tab надо нажать сразу после…
Ответ на Здравствуйте!Вопрос чисто… от ANTON
Tab надо нажать сразу после перехода на новую строку после class B, У вас там похоже нет отступа. Он только кажется.
Должно быть так:
Я не ясно выразился. Что…
Ответ на Tab надо нажать сразу после… от plustilino
Возможно CMD Windows для…
Ответ на Я не ясно выразился. Что… от ANTON
Возможно CMD Windows для этого не предназначена. В питоне, запущенном через терминал Linux, все работает.
Видимо так. Спасибо за…
Ответ на Возможно CMD Windows для… от plustilino
from random import randint…