Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand object oriented programming. How to use object composing?

Be the two classes Point() and Circle() defined below:

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)

I've tried to develop the method move(x,y) in the Circle calling the method move(x,y) from the class Point, without using inheritance. Firstly an object was initialized:

circle1 = Circle(4,1,1)

but when you use circle1.move(2,2) the circle position is still (1,1): What is wrong? I want to use _x and _y to simulates private variables!

like image 341
Laurinda Souza Avatar asked Dec 31 '22 04:12

Laurinda Souza


1 Answers

The problem there is that you are not using "composition" - you are just randomly calling Point.move from within a method in your circle class. That call will fail, as it would fail if placed anywhere else: Point.move is an instance method, and it needs an instance of point to work on.

For "composition" you need to have instances of the other class that are attributes on your class - and then you call upon the methods on those instances when required.

For example, your "Circle" could have a ".center" attribute that is a point. And then, you just call circle.center.move in an instance - or, if you want a .move method in the circle class itself:


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}"

What you were trying to do

Your code attempted to call a method in Point passing an instance of Circle. While Python 3 allows this, it is a sheer coincidence that it almost worked (if the "x" and "y" names in Circle where the same as in "Point" it would have worked as it is demonstrated in Dani's answer) - but that is not "OOP" nor "Composition", and just does not raise a runtime error because Python 3 treats instance methods in classes just as functions.

A "circle" is not a "point". Thinking about geometry, you could say that "Circle" and "Point" partake some attributes and methods - Python allow you to share those by using multiple inheritance, and what we call "mixins" - - So you could have a "LocationMixin" class thatwould have the "x" and "y" attributes, and the "move" method and be an ancestor of both "Point" and "Circle", and that would work.

Simplifying code

Your composition problem apart, it is important to note that in Python it is not important to try to make attributes "private", and define getters and setters for public consunption - Your point class would work just as well if it is simply written as:

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

The getter and setter will make sense if you want to validate that x and y ae set to numbers, or validate value ranges - otherwise, they are just redundant.

An interesting thing is that Python designs its "properties" so that you can change the attributes to have the getters and setters and add these validations on a later point, without breaking any compatibility with the previous versions of the class (Point) .

like image 168
jsbueno Avatar answered Jan 05 '23 18:01

jsbueno