Reputation: 1337
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!
Upvotes: 4
Views: 105
Reputation: 11
@jsbueno has the most design-wise correct answer. If you go ahead and use Point.move() you are using move() as a static method, instead of an instance method. That will pass the Circle 'self' object to the Point.move() method. If you follow the suggestions of the other posts to change self.x to self._x etc. it will work but it is a very bad design. It works because it treats the 'self' variable inside Point as a third argument along with x and y, instead of the actual Point object. You can rewrite for example your Point.move() method to something like this that would would still be working for moving the Circle object, but is badly designed:
def move2(foo,x,y):
foo._x = x
foo._y = y
print(foo._x,foo._y)
return foo._x,foo._y
Also the following is a link where I found the difference in static methods vs class methods while trying to answer your question :) CLASS METHOD VS STATIC METHOD 2020
Upvotes: 1
Reputation: 110311
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}"
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.
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) .
Upvotes: 5
Reputation: 723
You are not manipulating the right variable.
You circle class uses self.x
and self.y
but in the call to Point.move(self, x, y)
you manipulate self._x
and self._y
instead
Upvotes: 1
Reputation: 61910
Change the Circle
class to:
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}"
Example
circle1 = Circle(4, 1, 1)
print(circle1)
circle1.move(2, 2)
print(circle1)
Output
radius = 4,
x = 1,
y = 1
radius = 4,
x = 2,
y = 2
Otherwise, when Point.move
is executed two new fields (_x, _y
) are created, see:
circle1 = Circle(4, 1, 1)
circle1.move(2, 2)
print(circle1._x, circle1._y)
Output
2 2
Upvotes: 2