Попытка понять объектно-ориентированное программирование. Как использовать составление объектов? - PullRequest
4 голосов
/ 05 апреля 2020

Будь двумя классами Point() и Circle(), определенными ниже:

class Point:
    def __init__(self, x, y):
        self._x = x
        self._y = y

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, x):
        self._x = x

    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, y):
        self._y = y

    def __repr__(self):
        return f"{self._x}, {self._y}"

    def move(self, x, y):
        self._x = x
        self._y = y
        return self._x, self._y


class Circle:
    def __init__(self, radius, x, y):
        self._radius = radius
        self.x = x
        self.y = y

    def move(self, x, y):
        Point.move(self, x, y)

    def __repr__(self):

        return f"radius = {self._radius}, \nx = {self.x},\ny = {self.y}"


point1 = Point(12, 3)
print(point1)
point1.y = 0
print(point1)

point1.move(55, 7)
print(point1)

circle1 = Circle(4, 1, 1)
print(circle1)
circle1.move(2, 2)
print(circle1)

Я пытался разработать метод move(x,y) в Круге, вызывающий метод move (x, y) из Класс Point, без использования наследования. Сначала объект был инициализирован:

circle1 = Circle(4,1,1)

, но когда вы используете circle1.move(2,2), позиция круга все еще (1,1): что не так? Я хочу использовать _x и _y для имитации приватных переменных!

Ответы [ 4 ]

5 голосов
/ 05 апреля 2020

Проблема в том, что вы не используете «состав» - вы просто случайным образом вызываете Point.move из метода в вашем классе круга. Этот вызов потерпит неудачу, как и если бы он был помещен где-либо еще: Point.move - это метод экземпляра, и ему нужен экземпляр точки для работы.

Для "композиции" вам нужно иметь экземпляры другой класс, являющийся атрибутами вашего класса, а затем при необходимости вы вызываете методы в этих экземплярах.

Например, ваш «Круг» может иметь атрибут «.center», который является точкой. И затем, вы просто вызываете circle.center.move в экземпляре - или, если вам нужен метод .move в самом классе круга:


class Circle:
    def __init__(self, radius, x, y):
        self._radius = radius
        self.center = Point(x, y)

    def move(self, x, y):
        self.center.move(x, y)

    def __repr__(self):
        return f"Circle center at ({self.point}), radius: {self.radius}"

То, что вы пытались сделать

Ваш код попытался вызвать метод в Point, передав экземпляр Circle. Хотя Python 3 позволяет это, это чистое совпадение, что это почти сработало (если имена «x» и «y» в Circle, где то же самое, что и в «Point», это сработало бы, как показано в ответе Дани) - но это не "OOP" и не "Composition", и просто не вызывает ошибку времени выполнения, потому что Python 3 рассматривает методы экземпляра в классах как функции.

«Круг» - это не «точка». Думая о геометрии, вы можете сказать, что «Круг» и «Точка» разделяют некоторые атрибуты и методы - Python позволяют вам делиться ими, используя множественное наследование, и то, что мы называем «миксинами» - - так что вы можете иметь «LocationMixin» класс, который будет иметь атрибуты «x» и «y», а также метод «move» и будет предком «Point» и «Circle», и это будет работать.

Упрощенный код

Помимо проблем с составлением, важно отметить, что в Python не важно пытаться сделать атрибуты "приватными" и определять методы получения и установки для публикации c consunption - Ваш класс точек будет работать так же, как хорошо, если это просто записать как:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"{self.x}, {self.y}"

    def move(self, x, y):
        self.x = x
        self.y = y
        return self.x, self.y

Получатель и установщик будут иметь смысл, если вы захотите проверить, что x и y установлены как числа, или проверить диапазоны значений - в противном случае они просто избыточны.

Интересно то, что Python проектирует его "свойства", так что вы можете изменить атрибуты, чтобы получить геттеры и сеттеры и добавить t эти проверки на более позднюю точку, не нарушая совместимость с предыдущими версиями класса (точка).

2 голосов
/ 05 апреля 2020

Измените класс Circle на:

class Circle:
    def __init__(self, radius, x, y):
        self._radius = radius
        self._x = x
        self._y = y

    def move(self, x, y):
        Point.move(self, x, y)

    def __repr__(self):
        return f"radius = {self._radius}, \nx = {self._x},\ny = {self._y}"

Пример

circle1 = Circle(4, 1, 1)
print(circle1)
circle1.move(2, 2)
print(circle1)

Выход

radius = 4, 
x = 1,
y = 1
radius = 4, 
x = 2,
y = 2

В противном случае при выполнении Point.move создаются два новых поля (_x, _y), см.

circle1 = Circle(4, 1, 1)
circle1.move(2, 2)
print(circle1._x, circle1._y)

Выход

2 2
1 голос
/ 05 апреля 2020

@ jsbueno имеет наиболее продуманный правильный ответ. Если вы go впереди и используете Point.move (), вы используете move () как метод stati c вместо метода экземпляра. Это передаст объект Circle 'self' методу Point.move (). Если вы следуете советам других постов, измените self.x на self._x et c. это будет работать, но это очень плохой дизайн. Это работает, потому что обрабатывает переменную «self» внутри Point как третий аргумент вместе с x и y вместо фактического объекта Point. Например, вы можете переписать ваш метод Point.move () примерно так, чтобы он работал при перемещении объекта Circle, но он плохо спроектирован:

def move2(foo,x,y):
    foo._x = x
    foo._y = y
    print(foo._x,foo._y)
    return foo._x,foo._y

Также ниже приведена ссылка, где я обнаружил разницу в методах stati c и методах класса, пытаясь ответить на ваш вопрос :) МЕТОД КЛАССА ПРОТИВ СТАТИ C МЕТОД 2020

1 голос
/ 05 апреля 2020

Вы не управляете правильной переменной.
Вы обводите класс, используя self.x и self.y, но при вызове Point.move(self, x, y) вы манипулируете self._x и self._y вместо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...