10.31 Объектная модель Python
Превращение функции-атрибута класса в метод объекта при инстанциировании (пример из лекции)
реализуйте класс Rectangle для прямоугольника, стороны которого параллельны координатным осям. В классе должны быть:
поля x1, y1, x2, y2, хранящие координаты "левой нижней" и "правой верхней" вершин (т.е. x1<x2, y1<y2; проверять эти условия при инициализации не требуется)
метод __init__(), в который передаются значения x1, y1, x2, y2
метод __str__(), формирующий строку с координатами четырех вершин прямоугольника, т.е. строку вида "(x1,y1)(x1,y2)(x2,y2)(x2,y1)"
класс пригодится для остальных упражнений
Поля класса vs поля объекта (видимость) — пример из лекции
в классе Rectangle создайте поле rectcnt (счетчик объектов этого класса, первоначально 0). Добавьте в метод __init__() увеличение этого поля на 1, проверьте, что после создания N объектов оно равно N. Доработайте метод __str__(), чтобы он включал в строку значение поля rectcnt.
Подсказка — надо ходить либо к полю класса непосредственно, либо сделать rectcnt списком из одного элемента, и менять этот элемент
Использование setattr() / getattr() / .__dict__
Добавьте в метод __init__() создание поля rect_<k>, где k - номер объекта Rectangle в нумерации с 1. Создайте несколько объектов Rectangle; проверьте при помощи dir(<объект>), что в каждом из объектов есть поле rect_<k> с соответствующим значением k.
поле создавайте через setattr(); желающие могут попробовать напрямую модифицировать self.__dict__
в классе Rectangle создайте методы для операций сравнения "<" (__lt__()) и "==" (__eq__()); эти операции должны сравнивать числа - площади прямоугольников.
в классе Rectangle создайте методы для операций умножения на число. Операция должна возвращать объект Rectangle, все координаты вершин которого умножены на это число. Реализуйте:
"обычное" умножение, вида myrect * 5 (через __mul__())
"правое" умножение, вида 5 * myrect (через __rmul__())
Класс с простейшим __getitem__() (например, выдает k-й символ из строкового поля), показать как работает итератор.
в классе Rectangle создайте метод для операции доступа по индексу (__getitem__()). По индексам от 0 до 3 должны возвращаться кортежи-координаты вершин, соответственно, (x1,y1); (x1,y2); (x2,y2); (x2,y1).
Согласно протоколу итерации, такой объект должен получиться итерируемым (с исключением IndexError в качестве конца итерации). Проверьте это: превратите прямоугольник в список или выведите координаты вершин в цикле.
в классе Rectangle создайте метод __bool__(), возвращающий False для прямоугольников площади 0, и True в остальных случаях. Проверьте работу этого метода в операторе if.
в классе Rectangle создайте метод __del__(), который уменьшает на 1 значение поля класса rectcnt и выводит уменьшенное значение. Создайте функцию с локальным экземпляром Rectangle и проверьте, что метод __del__() вызывается при выходе из функции.
Д/З
Написать класс Omnibus
экземпляры которого (обозначим экземпляр omn) обладают следующими свойствами
Для любого атрибута (если его имя не начинается на "_", такие имена не входят в тесты) можно написать операцию omn.атрибут = значение (значение не хранится и не используется, см. далее)
После этого выражение omn.атрибут будет возвращать количество экземпляров класса, для которых было таким способом задано поле атрибут
Операция del omn.атрибут отменяет действие пункта 1 (поле атрибут считается не заданным для данного объекта, у других объектов оно уменьшается на 1).
- Если соответствующего атрибута у объекта нет, ничего не происходит.
Input
1 a, b, c = Omnibus(), Omnibus(), Omnibus() 2 del a.random 3 a.i = a.j = a.k = True 4 b.j = b.k = b.n = False 5 c.k = c.n = c.m = hex 6 print(a.i, a.j, a.k, b.j, b.k, b.n, c.k, c.n, c.m) 7 del a.k, b.n, c.m 8 print(a.i, a.j, b.j, b.k, c.k, c.n) 9 del a.k, c.m 10 print(a.i, a.j, b.j, b.k, c.k, c.n) 11 a.k = b.i = c.m = 777 12 print(a.i, a.j, a.k, b.j, b.k, c.k, c.n, c.m)
Output
1 2 3 2 3 2 3 2 1 1 2 2 2 2 1 1 2 2 2 2 1 2 2 3 2 3 3 1 1
- В каждом из собственных тестов надо проверять все три свойства этого класса
Написать класс Triangle, треугольника на плоскости,
- в котором будут:
Конструктор по трём точкам на плоскости (три последовательности неотрицательных чисел из двух элементов каждая, не забыть преобразовать их в tuple или list)
Площадь треугольника: abs(треугольник), равна нулю, если треугольник невозможен (реализуется с помощью .__abs__())
Треугольник с нулевой площадью — пустой (реализуется с помощью метода .__bool__())
Сравнение треугольников на "<" — это сравнение площадей
Булевская операция треугольник1 in треугольник2 — проверка того, что один треугольник целиком содержится во втором (в частности, треугольник содержится сам в себе, а пустой треугольник содержится в любом)
Булевская операция треугольник1 & треугольник2 — проверка того, что два треугольника пересекаются (треугольник с нулевой площадью не имеет точек и ни с чем не пересекается)
Input
1 r = Triangle((4, 2), (1, 3), (2, 4)) 2 s = Triangle((1, 1), (3, 1), (2, 2)) 3 t = Triangle((0, 0), (2, 3), (4, 0)) 4 o = Triangle((1, 1), (2, 2), (3, 3)) 5 print(*(f"{n}({bool(x)}):{round(abs(x), 3)}" for n, x in zip("rsto", (r, s, t, o)))) 6 print(f"{s < t=}, {o < t=}, {r < t=}, {r < s=}") 7 print(f"{s in t=}, {o in t=}, {r in t=}") 8 print(f"{r & t=}, {t & r=}, {s & r=}, {o & t=}")
Output
r(True):2.0 s(True):1.0 t(True):6.0 o(False):0 s < t=True, o < t=True, r < t=True, r < s=False s in t=True, o in t=True, r in t=False r & t=True, t & r=True, s & r=False, o & t=False
- В каждом из собственных тестов надо проверять все шесть свойств треугольника. Координаты в тестах предполагаются целочисленными. Точность вещественных чисел (например, площади) при выводе ограничивать до трёх знаков после запятой, как в примере.
- в котором будут:
Написать класс Maze(M), задающий квадратный лабиринт размера N×N. По умолчанию между комнатами лабиринта прохода нет. Конструкция M[x0,y0 : x1,y1] = "·" открывает проход попарно между всеми соседними комнатами от x0, y0 до x1, y1 включительно при условии совпадения абсциссы или ординаты (порядок может быть любым), в противном случае ничего не происходит. Конструкция M[x0,y0 : x1,y1] = "█" закрывает проходы при тех же условиях. Конструкция M[x0,y0 : x1,y1] возвращает True, если комната x1, y1 достижима из x0, y0, в противном случае — False. Строковое представление лабиринта 4×4 (в начальном варианте со всеми стенами, и после открытия некоторых проходов) см. в примере.
- Используйте приведённые символы
«·» (MIDDLE DOT) обозначает центр комнаты или проход
«█» (FULL BLOCK) обозначает непроходимую стену
Если в вашем редакторе/IDE они не отображаются — это повод его настроить и/или сменить.
Исследуйте, что именно получают методы .__getitem__() / .__setitem__() в этих конструкциях
- Ошибки (выход за границы лабиринта, попытка присвоить неправильные символы и т. п.) не обрабатывать и не тестировать.
возможно, вам пригодится разбор классической задачи про обход лабиринта
- не забудьте только, что не стоит хранить информацию о просмотре в самом лабиринте
я просто сделал ещё одно множество — все просмотренные вершины — и проверял наличие x1, y1 в нём
Input
Output
█████████ █·█·█·█·█ █████████ █·█·█·█·█ █████████ █·█·█·█·█ █████████ █·█·█·█·█ █████████ False False False █████████ █·█·█···█ █·█·█·█·█ █·█·█·█·█ █·█·█·█·█ █·█·█·█·█ █·█████·█ █·······█ █████████ False True True
- Используйте приведённые символы