Расширим функционал класса написанного вами в предыдущей задаче.
Реализуйте методы:
get_pos()
— возвращает координаты верхнего левого угла в виде кортежа;get_size()
— возвращает размеры в виде кортежа;move(dx, dy)
— изменяет положение на заданные значения;resize(width, height)
— изменяет размер (положение верхнего левого угла остаётся неизменным).
Примечание
Ваше решение должно содержать только классы и функции.
В решении не должно быть вызовов инициализации требуемых классов.
Все результаты вычислений нужно округлить до сотых.
Пример
Ввод
rect = Rectangle((3.2, -4.3), (7.52, 3.14))
print(rect.get_pos(), rect.get_size())
rect.move(1.32, -5)
print(rect.get_pos(), rect.get_size())
Вывод
(3.2, 3.14) (4.32, 7.44)
(4.52, -1.86) (4.32, 7.44)
Ввод
rect = Rectangle((7.52, -4.3), (3.2, 3.14))
print(rect.get_pos(), rect.get_size())
rect.resize(23.5, 11.3)
print(rect.get_pos(), rect.get_size())
Вывод
(3.2, 3.14) (4.32, 7.44)
(3.2, 3.14) (23.5, 11.3)
Решение
Очень хорошее развитие предыдущей задачи. С одной стороны можно продолжать дорабатывать код из первой задачи, а с другой стороны можно заметить, Что нас подталкивают к тому, чтобы перейти от описания прямоугольника в виде пары координат к описанию “левый верхний угол, высота, ширина”.
На это указывает условие реализации метода resize(). Напомню, что там предлагают, чтобы левая верхняя точка оставалась в неизменном положении.
Это, в самом деле , более удобный подход, в чем можно будет убедиться в следующем задании.
И хотя мы могли бы использовать метод move() класса Point(), мы не будем этого делать, потому что в этом нет большого смысла. В классе Point() метод move() перемещает точку в новые координаты, а нам требуется ее сместить относительно текущего положения. Все это приводит к тому, что мы так или иначе будем вынуждены вычислить новые координаты, но вместо вызова метода move() точки из метода move() нашего прямоугольника, будет проще модифицировать координаты угла напрямую. При этом надо отдавать себе отчет, что это не является хорошей практикой в ООП.
Хорошей практикой ООП было бы использование методов, считывающих и устанавливающих координаты точки, но, с другой стороны, так как вызов функции – одна из самых дорогих операций в python, при больших объемах вычислений, этой практикой можно пренебречь.
Посмотреть код
Решение
# продолжение первого, наивного решения
class Rectangle:
def __init__(self, corner1, corner2) -> None:
self.left_down = [min(corner1[0], corner2[0]),
min(corner1[1], corner2[1])]
self.up_right = [max(corner1[0], corner2[0]),
max(corner1[1], corner2[1])]
def perimeter(self):
return round((self.up_right[0] - self.left_down[0]) * 2 +
(self.up_right[1] - self.left_down[1]) * 2, 2)
def area(self):
return round((self.up_right[0] - self.left_down[0]) *
(self.up_right[1] - self.left_down[1]), 2)
def get_pos(self):
return round(self.left_down[0], 2), round(self.up_right[1], 2)
def get_size(self):
return round(self.up_right[0] - self.left_down[0], 2), \
round(self.up_right[1] - self.left_down[1], 2)
def move(self, dx, dy):
self.left_down[0] += dx
self.left_down[1] += dy
self.up_right[0] += dx
self.up_right[1] += dy
def resize(self, width, height):
self.up_right[0] = self.left_down[0] + width
self.left_down[1] = self.up_right[1] - height
Решение
# решение на основе класса Point
class Point:
def __init__(self, x, y) -> None:
self.x = x
self.y = y
self.round()
def round(self):
self.x = round(self.x, 2)
self.y = round(self.y, 2)
return self
class Rectangle:
def __init__(self, corner1, corner2) -> None:
self.corner = Point(min(corner1[0], corner2[0]), max(corner1[1], corner2[1])) # noqa
self.width = round(max(corner1[0], corner2[0]) - self.corner.x, 2)
self.height = round(self.corner.y - min(corner1[1], corner2[1]), 2)
def perimeter(self):
return round((self.width + self.height) * 2, 2)
def area(self):
return round(self.width * self.height, 2)
def get_pos(self):
return self.corner.x, self.corner.y
def get_size(self):
return self.width, self.height
def move(self, dx, dy):
self.corner.x += dx
self.corner.y += dy
self.corner.round()
def resize(self, width, height):
self.width = round(width, 2)
self.height = round(height, 2)
class Rectangle:
def __init__(self, tuple1, tuple2):
self.x1 = tuple1[0]
self.y1 = tuple1[1]
self.x2 = tuple2[0]
self.y2 = tuple2[1]
self.widt = round((abs(self.x2 – self.x1)), 2)
self.heiht = round((abs(self.y1 – self.y2)), 2)
def perimeter(self):
return round((2 * (abs(self.x2 – self.x1)) + 2 * (abs(self.y2 – self.y1))), 2)
def area(self):
return round((abs(self.x2 – self.x1) * abs(self.y2 – self.y1)), 2)
def get_pos(self):
a = (round(min(self.x1, self.x2), 2), round((max(self.y1, self.y2)), 2))
return a
def get_size(self):
b = (round(self.widt, 2), round(self.heiht, 2))
return b
def move(self, dx, dy):
self.x1 += dx
self.x2 += dx
self.y1 += dy
self.y2 += dy
def resize(self, width, height):
self.widt = round(width, 2)
self.heiht = round(height, 2)
может подскажет кто, что не так в этом решении, проходит только первые два теста
Вероятнее всего проблема в resize который не изменяет координаты одной из точек. Если сначала выполнить resize, а потом попросить посчитать площаль или периметр, получится неправильный ответ.
Стоит пересмотреть подход и все делать только через координаты точек, или только через точка + ширина/высота.
Не очень понимаю, почему в задаче в методе resize() нельзя просто назначать новые width и height, а потом их же и выводить? Положение верхнего левого угла остается неизменным, так какая разница какие там координаты у других углов?
Но из-за этого у меня явно не проходит проверку код
class Rectangle:
def __init__(self, corner_1: tuple, corner_2: tuple):
self.corner_1 = corner_1
self.corner_2 = corner_2
self.left_x = min(self.corner_1[0], self.corner_2[0])
self.left_y = max(self.corner_1[1], self.corner_2[1])
self.width = abs(self.corner_2[0] – self.corner_1[0])
self.length = abs(self.corner_2[1] – self.corner_1[1])
def get_size(self):
return tuple((round(self.width, 2), round(self.length, 2)))
def move(self, dx, dy):
self.left_x += dx
self.left_y += dy
def get_pos(self):
return tuple((round(self.left_x, 2), round(self.left_y, 2)))
def resize(self, width, height):
self.width = width
self.length = height
Почему нельзя? Можно. Второе решение в статье основано именно на этом принципе.
Моя проблема оказалась в другом: это задание, наследуя предыдущее, также требует наличия методов perimeter и area. По каким-то причинам для меня это оказалось неочевидно, хотя в начале и написано, что нужно “дополнить” класс из предыдущего задания
Сергей, спасибо! Все так
Всем привет!
Подскажите, пожалуйста, в чем может быть проблема? Отталкивался от изменения координат точек. Первые два теста проходит, но на третьем ошибка исполнения (RE)
class Rectangle():
def __init__(self, point_1, point_2):
self.point_min_x = min(point_1[0], point_2[0])
self.point_min_y = min(point_1[1], point_2[1])
self.point_max_x = max(point_1[0], point_2[0])
self.point_max_y = max(point_1[1], point_2[1])
def get_pos(self):
”’координаты верхнего левого угла в виде кортежа”’
return (self.point_min_x, self.point_max_y)
def get_size(self):
”’возвращает размеры в виде кортежа”’
self.size_x = round(abs(self.point_max_x – self.point_min_x), 2)
self.size_y = round(abs(self.point_max_y – self.point_min_y), 2)
return (self.size_x, self.size_y)
def move(self, dx, dy):
”’изменяет положение на заданные значения”’
self.point_min_x = round((self.point_min_x + dx), 2)
self.point_max_x = round((self.point_max_x + dx), 2)
self.point_min_y = round((self.point_min_y + dy), 2)
self.point_max_y = round((self.point_max_y + dy), 2)
def resize(self, width, height):
”’изменяет размер (положение верхнего левого угла остаётся неизменным)”’
self.point_max_x = round((self.point_min_x + width), 2)
self.point_min_y = round((self.point_max_y – height), 2)
Насколько я понимаю, в условии задачи сказано, что мы расширяем функционал нашего класса, а это значит, что надо оставить ранее написанные методы.
Добавил два метода, но не помогло. Так же на третьем примере вылетает ошибка исполнения (RE)
Покажите весь код. Его лучше оформлять через виджет вставки кода.
В данном коде проблема в том, что perimeter и size возвращают указатель на функцию, к которому пытаются применить round.
Eсли self.perimeter заменить на perimeter, то ошибка будет на 20м тесте с диагнозом WA, что в нашем случае означает неточность вычислений.
В 20м тесте по какой-то причине яндексы не любят, когда округляется результат resize, если избавиться от round, то все срабатывает.
Я сдался на 20-ом примере. Никак не хочет проходить его. Через f форматирование слетает на 7 тесте, при умножении на 100 и округлении через int получается некорректный результат, а при округлении через round снова хрень) Пойду дальше, а эту задачу может как-нибудь потом попробую добить, если не брошу изучение.
Огромное спасибо за быстрые ответы!
Достаточно сделать так:
Скорее всего у них в тесте нет округления для этой операции.
В a у вас лежит ранее округленный результат, который в случае print выведет число в формате X,XX, b вы получаете в качестве аргумента. Математическая правильность их суммы в этой ситуации зависит от значений a и b.
Так к примеру при a = 1.05 и b = 0.58 все хорошо, но при a = 1.05 и b = 0.59,
round(a, 2) + b=1.6400000000000001
round(a + b, 2)=1.64
Сергей, здравствуйте.
Скажите, пожалуйста, свое мнение по поводу приведенного ниже кода. Яндекс его принимает.
Он похож на Ваш 2й вариант, но здесь верхний левый угол не определяется как экземпляр класса Point (соответственно, отсюда следуют различия с Вашим кодом).
Я просто хочу понять, есть ли какие-то недостатки в таком (моем) варианте? И, если более рациональным всё-таки является отнесение левого верхнего угла к отдельному классу (как у Вас), то почему?
А еще в этом случае (как в Вашем, так и в моем варианте) наличие или отсутствие округления в resize на результат не влияет – Яндекс принимает всё. Вероятно, потому, что в данном случае не проводятся никакие вычисления, а просто принимаются новые значения width и height (а они соответствуют «разрешенным» параметрам – в них исходно не более 2-х знаков после точки). Правильно ли я понимаю?
Я использовал класс Point только для того, чтобы показать как в классе можно использовать ранее написанные классы без наследования. В следующей задачи класс прямоугольника будет уже наследован от точки.
в принципе, наследование или даже простое использование класса Point позволяет сделать код более читабельным. Согласитесь, что self.left_up.x более понятно, чем self.left_up[0]?
Тот факт, что во втором решении в методе size нет необходимости делать округление может говорить нам, например, о том, что туда всегда приходят валидные данные, либо о том, что у яндекса нет теста, который проверял бы на этом этапе какие размеры в итоге установлены. Впрочем, возможны и другие варианты развития событий. Что там происходит на самом деле знают лишь составители тестов.
В то же время, если подойти к решению задачи другим путем, описывая прямоугольник через две противоположные точки, округление после resize не дает пройти 20 тест. Из чего можно сделать вывод, что в нем используется такая комбинация правой нижней координаты и размеров, что обычное сложение дает погрешность и эта погрешность не округляется. Этот момент обсуждается в комментарии чуть выше.
Большое спасибо за подробный ответ!
Согласна, self.left_up.x, наверное,более читабельно, чем self.left_up[0].
С тестами Яндекса тоже понятно. Комментарии выше читала, но теперь, благодаря Вашему ответу, окончательно всё разложилось “по полочкам”.