Reputation: 47
Circle class uses a Point object as the center. A @classmethod called from_tuple is needed to allow Circle objects to be created with tuple instead of a Point as the center. This is how the output should look like for from_tuple()
I apologize for the messy codes, having two getters and one setter for center. I got really confused toward the end, knowing that center must be a tuple. Please help me with resolving the AttributeError in the getter when accessing from_tuple().
Error message is shown below
>>> center_point=3,4
>>> circle=Circle.from_tuple(center=center_point)
>>> circle
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Users\hole1\Desktop\ProgramIntro\shapes.py", line 54, in __repr__
return "Circle(center=Point({0}, {1}), radius={2})".format(self.center[0],self.center[1],self.radius)
File "C:\Users\hole1\Desktop\ProgramIntro\shapes.py", line 65, in center
return(self._center)
AttributeError: 'Circle' object has no attribute '_center'
The below is class Point:
import math
class Point:
def __init__(self,x=0,y=0):
self.x = x
self.y = y
self.data=[x,y]
def __getitem__(self, index):
return self.data[index]
def __iter__(self):
yield self.x
yield self.y
def __add__(self,other):
return Point(self.x+other.x,self.y+other.y)
def __mul__(self,n):
return Point(self.x*n,self.y*n)
def __rmul__(self,n):
return Point(n*self.x,n*self.y)
@classmethod
def from_tuple (cls, self=(0,0)):
return cls(*self)
def loc_from_tuple(self,t=(0,0)):
self.x=t[0]
self.y=t[1]
def __str__(self):
return "Point at ({0}, {1})".format(self.x,self.y)
def __repr__(self):
return"Point(x={0}, y={1})".format(self.x,self.y)
The below is class Circle.
class Circle(Point):
def __init__(self, center=(0,0), radius=1):
try:
x, y = center
super(Circle, self).__init__(x,y)
Point.__init__(self,center)
self.radius=radius
self.center=center
except (UnboundLocalError, TypeError, AttributeError) as e:
pass
if not center == (0,0):
if not isinstance(center,Point):
if not isinstance(center, tuple):
raise TypeError("The center must be a oint!")
def __str__(self):
return "Circle with center at ({0}, {1}) and radius {2}".format(self.center.x, self.center.y, self.radius)
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})".format(self.center[0],self.center[1],self.radius)
@property
def center(self):
return self.x, self.y
@property
def center(self):
return(self._center)
@center.setter
def center(self, center):
if not center == (0,0):
if not isinstance(center,Point):
raise TypeError("The center must be a Point!")
self._center=center
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, radius):
if radius<0:
raise ValueError('The radius cannot be negative')
self._radius=radius
@classmethod
def from_tuple(cls, center=(0,0),radius=1):
return cls(center,radius)
Upvotes: 1
Views: 499
Reputation: 25789
There are a lot of things wrong with your code - from logical (i.e. why does a Circle
inherit from a Point
?) to structural (using undeclared instance properties) to downright weird (why are you defining your Circle.center
getter twice?) - but the main culprit for the error in question is the very bad idea of capturing all exceptions just to ignore them because if you haven't done so you'd notice the error yourself.
Namely, when you call your Circle.from_tuple()
class method, it in turn creates a new Circle
instance and its __init__()
method gets called with the passed passed center
and radius
(btw. why do you need Circle.from_tuple()
when you're just forwarding your arguments - why not just call Circle()
directly?). Given that you're passing a (3, 4)
as center
to it, it's as if you've effectively called: Circle.__init__(center=(3, 4))
.
And there you get a slew of problems. First, you call the super.__init__()
method to pass the center's x
and y
- fair enough, it will initiate self.x
, self.y
and self.data
(redundant, but lets not get into it). Then for some unknown reason you attempt to call it again, using a different syntax and passing your tuple only as the first parameter, which in turn now turns self.x
to have the value of center
((3, 4)
). But the real problem lies when you call your Circle.center()
setter passing it the same (3, 4)
tuple. If you look at your method:
@center.setter
def center(self, center):
if not center == (0, 0):
if not isinstance(center, Point):
raise TypeError("The center must be a Point!")
self._center = center
What do you think is going to happen? Given that the passed center
((3, 4)
) is not (0, 0)
, and most definitely not an instance of Point
(it's a tuple) it will raise a TypeError
and self._center
will never be set.
But wait, since you've decided to capture all UnboundLocalError
, TypeError
and AttributeError
exceptions in your Circle.__init__()
and just pass them through you never get informed that your center
wasn't set, so when you attempt to print it out in REPL Circle.__repr__()
gets called, which in turn attempts to access the undefined Circle._center
and you get your nice error.
A quick & dirty solution, since you're already inheriting from Point
, is to just forget about the center altogether - you already have self.x
and self.y
(and self.data
being a list based on those) so remove any references to center
and you're golden:
class Circle(Point):
def __init__(self, center=(0, 0), radius=1):
super(Circle, self).__init__(*center)
self.radius = radius
def __str__(self):
return "Circle with center at ({0}, {1}) and radius {2}".format(self.x, self.y, self.radius)
def __repr__(self):
return "Circle(center=Point({0}, {1}), radius={2})".format(self.x, self.y, self.radius)
@property
def center(self):
return Point(self.x, self.y)
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, radius):
if radius < 0:
raise ValueError('The radius cannot be negative')
self._radius = radius
And now you can instantiate it both with a Point
or a tuple
for its center.
But even better solution would be to rethink what are you trying to achieve here and, when you get stuck, to attempt good old Rubber duck debugging to capture errors such as the one in your code.
Upvotes: 1