Oop for class circle - python

Im writing a class in python called Circle.
Now as part of the class I want to define methods so I did but when I run the program it crashes and says they are not defined. I cant find the problem.
class Circle():
""" Holds data on a circle in the plane """
def __init__(self,*args):
if isinstance(args[0],Point) and isinstance(args[1],(float,int)):
assert args[1]>0
self.center= args[0]
self.r= args[1]
else:
assert args[2]>0
self.a=args[0]
self.b=args[1]
self.center= Point(self.a,self.b)
self.r= args[2]
def __mul__(self,other):
assert isinstance(other,(float,int))
assert other>0
return Circle(self.center,self.r*other)
__rmul__= __mul__
def area(self):
return math.pi*self.r**2
def circumference(self):
return 2*self.r*math.pi
def move(self,p):
assert isinstance(p,Point)
self.center= p
return None
I wrote a class for Point aswell, so thats not the problem.
This is what happens when I run the porgram:
>>> a=Circle(-3,-3,1)
>>> area(a)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
area(a)
NameError: name 'area' is not defined

Edit: as #jsbueno points out, this was not the error causing your error message:
Your indentation is off (def __mul__ should be 1 space to the left), therefore Python thinks you have ended your class definition and are simply defining some more functions (not class methods).
Also, you should call area as a method - a.area(), not area(a).
I've done a bit of reworking - added some comments, renamed some variables, generally cleaned up - this now works properly:
from math import pi
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
class Circle:
"""
Holds data on a circle in the plane
"""
def __init__(self, a, b, c=None):
if c is None:
# Circle(Point, scalar)
self.center = a
self.r = b
else:
# Circle(scalar, scalar, scalar)
self.center = Point(a, b)
self.r = c
#property
def r(self):
return self._r
#r.setter
def r(self, new_r):
assert new_r > 0
self._r = new_r
def __mul__(self, scale_by):
return Circle(self.center, self.r * scale_by)
__rmul__ = __mul__
def area(self):
return pi * self.r**2
def circumference(self):
return 2 * pi * self.r
def move(self, new_center):
self.center = new_center
then
a = Circle(-3,-3,1)
print(a.area())
gives
3.141592653589793
which is correct.

The methods in a class are available to the instance, but they have to be called as components of the instance with the "." operator.
So, in your example, you should use
a = Circle()
a.area()
and not
area(a)

Related

python; how to pass one argument through multiple methods in a class

I am learning about class structure in python. Would like to know if it's possible to pass one argument through more than one method.
class Example(object):
def __init__(self, x):
self.x = x
def square(self):
return self.x**2
def cube(self):
return self.x**3
def squarethencube(y):
sq = Example.square(y)
cu = Example.cube(sq)
return cu
two = Example(2)
print(two.squarethencube())
Error is on line 10; AttributeError: 'int' object has no attribute 'x'
The goal is to use the 'squarethencube' method to pass '2' to square(), which is 4. Then pass '4' to cube(). The desired output is '64'. Obviously, you can write a function to do the math in a very simple way; the question here is how to use multiple methods.
I understand the error in that .x is getting assigned as an attribute onto the output of cube(sq). I was getting the same error, but on line 7, before I changed the argument to y (from self.x).
I've found some similar answers here but I need a simpler explanation.
Currently, square and cube are methods bound to the class; however, you are accessing them in squarethencube by class name, but they are methods, and thus rely on a reference to the class from an instance. Therefore, you can either create two new instances of the class or use classmethod:
Option1:
class Example(object):
def __init__(self, x):
self.x = x
def square(self):
return self.x**2
def cube(self):
return self.x**3
def squarethencube(self, y):
sq = Example(y).square()
cu = Example(y).cube()
return cu
Option 2: use a classmethod:
class Example(object):
def __init__(self, x):
self.x = x
#classmethod
def square(cls, x):
return x**2
#classmethod
def cube(cls, x):
return x**3
def squarethencube(self, y):
sq = Example.square(y)
cu = Example.cube(sq)
return cu
class Example:
def __init__(self, x):
self.x = x
def square(self):
return self.x**2
def cube(self):
return self.x**3
def squarethencube(self):
return (self.x**2)**3
two = Example(2)
print(two.squarethencube())

Force user to call Object :__call__

Is it possible to only allow a user invoke any/all object methods when the object itself is called?
Looking for different examples on how to do this
Some example code
class Test:
def __init__(self, x=1):
self.x = x
def __call__(self, x=1):
self.x = x
return self
def get(self, y):
return (self.x * y)
t_obj = Test()
t_obj(2).get(1) # Acceptable case
t_obj.get(1) # Raises exception
t_obj().get(2) # Acceptable case
Right way to do it
It seems to me that what you want is for t_obj to actually be the class and not an instance of Test. It then gives the exact behavior you showed in your example.
class Test:
def __init__(self, x=1):
self.x = x
def get(self, y):
return (self.x * y)
t_obj = Test # not an object, actually a class
t_obj(2).get(1) # 2
t_obj.get(1) # Raises exception
t_obj().get(2) # 2
In particular, the exception states that method get() must be called with Test instance, i.e. you had to call t_obj to instantiate an object before being able to call get(), which is exactly what you want.
Fun way to do it
Although, suppose you really need your objects to be callable, here is a hacky way to get this working. It replaces the get method of your object when called, replacing a placeholder which only purpose is to raise.
class Test:
def __init__(self, x=1):
self.x = x
def __call__(self, x):
self.x = x
self.get = lambda y: self.x * y
return self
def get(self, y):
raise AttributeError('call object first')
t_obj = Test()
t_obj.get(1) # Raises exception
t_obj(2)
t_obj.get(1) # 2

How to Nested Classes in Python when trying to write a __call__ overwrite

So hopefully below illustrates my point. I want to set the translate attributes once and then be able to pass any mods (like translate) into the modLevels function. The only way I know how to do this is through nested classes, but I can't figure out how to get access to the outer class points. Any ideas or maybe even let me know if I'm going about this all wrong. THANKS!
class PointSet:
def __init__(self, points):
self.points = points
class translate:
def __init__(self, xmove=0, ymove=0):
self.xmove = xmove
self.ymove = ymove
def __call__(self):
for p in Outer.points: # <-- this part isnt working
p.x += self.xmove; p.y += self.ymove
def modLevels(levels, *mods):
for lev in range(levels):
for mod in mods:
mod
set1 = PointSet(...list of point objects here...)
coolMod = translate(xmove=5)
change(5, coolMod)
Pass it as a parameter.
class PointSet:
def __init__(self, points):
self.points = points
class translate:
def __init__(self, xmove=0, ymove=0, parent):
self.parent = parent
self.xmove = xmove
self.ymove = ymove
def __call__(self):
for p in self.parent.points:
p.x += self.xmove; p.y += self.ymove
Self-contained example:
class A:
def __init__(self):
self.data = [1,2,3]
class B:
def __init__(self, parent):
self.data = [4,5,6]
self.parent = parent
def access(self):
print(self.parent.data)
a = A()
b = a.B(a)
b.access()
However, as explained in comments, you don't need a nested class at all.
class PointSet:
def __init__(self, points):
self.points = points
def translate(self, x, y):
for p in self.points:
p.x += x
p.y += y
Thank you all for your help. I found a way to access the outer class on ubuntu forums. Solved referencing outer class from an inner class.
I needed to do this to pass a few parameters to the translation constructor and then overwrite the call function to use those parameters. This is a similar concept to a C++ function object like what you would pass to an STL algorithm: more on function objects.

For Python 3.5, can a subclass be constructed from an alternate constructor of its superclass? [duplicate]

I have a parent class that is inherited by several children. I would like to initialize one of the children using the parent's #classmethod initializers. How can I do this? I tried:
class Point(object):
def __init__(self,x,y):
self.x = x
self.y = y
#classmethod
def from_mag_angle(cls,mag,angle):
x = mag*cos(angle)
y = mag*sin(angle)
return cls(x=x,y=y)
class PointOnUnitCircle(Point):
def __init__(self,angle):
Point.from_mag_angle(mag=1,angle=angle)
p1 = Point(1,2)
p2 = Point.from_mag_angle(2,pi/2)
p3 = PointOnUnitCircle(pi/4)
p3.x #fail
If you try to write __init__ like that, your PointOnUnitCircle has a different interface to Point (as it takes angle rather than x, y) and therefore shouldn't really be a sub-class of it. How about something like:
class PointOnUnitCircle(Point):
def __init__(self, x, y):
if not self._on_unit_circle(x, y):
raise ValueError('({}, {}) not on unit circle'.format(x, y))
super(PointOnUnitCircle, self).__init__(x, y)
#staticmethod
def _on_unit_circle(x, y):
"""Whether the point x, y lies on the unit circle."""
raise NotImplementedError
#classmethod
def from_angle(cls, angle):
return cls.from_mag_angle(1, angle)
#classmethod
def from_mag_angle(cls, mag, angle):
# note that switching these parameters would allow a default mag=1
if mag != 1:
raise ValueError('magnitude must be 1 for unit circle')
return super(PointOnUnitCircle, cls).from_mag_angle(1, angle)
This keeps the interface the same, adds logic for checking the inputs to the subclass (once you've written it!) and provides a new class method to easily construct a new PointOnUnitCircle from an angle. Rather than
p3 = PointOnUnitCircle(pi/4)
you have to write
p3 = PointOnUnitCircle.from_angle(pi/4)
You can override the subclass's __new__ method to construct instances from the superclass's alternate constructor as shown below.
import math
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
#classmethod
def from_polar(cls, radius, angle):
x = radius * math.cos(angle)
y = radius * math.sin(angle)
return cls(x, y)
class PointOnUnitCircle(Point):
def __new__(cls, angle):
point = Point.from_polar(1, angle)
point.__class__ = cls
return point
def __init__(self, angle):
pass
Note that in __new__, the line point = Point.from_polar(1, angle) cannot be replaced by point = super().from_polar(1, angle) because whereas Point sends itself as the first argument of the alternate constructor, super() sends the subclass PointOnUnitCircle to the alternate constructor, which circularly calls the subclass's __new__ that calls it, and so on until a RecursionError occurs. Also note that even though __init__ is empty in the subclass, without overriding __init__ in the subclass, the superclass's __init__ would automatically be called immediately after __new__, undoing the alternate constructor.
Alternatively, some object designs are simpler with composition than with inheritance. For example, you could replace the above PointOnUnitCircle class without overriding __new__ with the following class.
class UnitCircle:
def __init__(self, angle):
self.set_point(angle)
def set_point(self, angle):
self.point = Point.from_polar(1, angle)

Need to create class Route and Point

I'm struggling with this easy assignment. I need to create 2 classes, Point and Route. I need to add/remove Points to/from Route and then calculate the sum distance between the Points in the Route.
So far my code is this:
import math
class Point:
"Two-dimensional points"
def __init__(self, x=0.0, y=0.0):
self._x = x
self._y = y
def __str__(self):
result = "\n".join(["x: %f" % self.x(),
"y: %f" % self.y(),
"rho: %f" % self.rho(),
"theta: %f" % self.theta()])
return result
# Queries
def x(self):
"Abscissa"
return self._x
def y(self):
"Ordinate"
return self._y
def rho(self):
"Distance to origin (0, 0)"
return math.sqrt(self.x()**2 + self.y()**2)
def theta(self):
"Angle to horizontal axis"
return math.atan2(self.y(), self.x())
def distance(self, other):
"Distance to other"
return self.vectorTo(other).rho()
def vectorTo(self, other):
"Returns the Point representing the vector from self to other Point"
return Point(other.x() - self.x(), other.y() - self.y())
# Commands
def translate(self, dx, dy):
"Move by dx horizontally, dy vertically"
self._x += dx
self._y += dy
def scale(self, factor):
"Scale by factor"
self._x *= factor
self._y *= factor
def centre_rotate(self, angle):
"Rotate around origin (0, 0) by angle"
temp_x = self.rho() * math.cos(self.theta() + angle)
temp_y = self.rho() * math.sin(self.theta() + angle)
self._x, self._y = temp_x, temp_y
def rotate(self, p, angle):
"Rotate around p by angle"
self.translate(-p.x(), -p.y())
self.centre_rotate(angle)
self.translate(p.x(), p.y())
class Route:
def __init__(self):
self.Point = []
def __add__ (x,y,index):
self.points.insert(Point(x,y), index)
The Point works fine but I can't figure out how to get Route to work.
The error I get is:
>>>
>>> route = Route()
>>> route.add(32, 12, 2)
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
route.add(32, 12, 2)
File "C:\Users\John Wayne\Dropbox\kool\geom.py", line 73, in add
self.points.insert(Point(x, y), index)
TypeError: 'Point' object cannot be interpreted as an integer
>>>
OK I have managed to fix the Route class as following:
class Route:
def __init__(self):
self.points = []
def add_point(self, x, y, index):
self.points.insert(index, Point(x,y))
But now I have problem with my method get_lenght:
def get_lenght(self, Point):
for Point in self.points:
What is wrong with this get_lenght method?
Thank you very much.
It looks like you are trying to insert in to the list self.points using the insert method. Insert takes two arguments:
my_list = []
my_list.insert(index, object)
Which will insert the object before the index supplied. The argument 'index' must be a number.
In your example, you have passed the object first and the index second. Switch the arguments around and it should be working fine. e.g.:
self.points.insert(index, Point(x,y))
Alternatively, if you just want to add the object on to the end of the list, the 'append' method may work better for you:
self.points.append(Point(x,y))
In addition, I have assumed that your class 'Route' is written as follows as the code copied above appears to be incorrect (your method 'add' shouldn't be defined with the underscored before and after, you didn't pass 'self' as the first argument to your add method, and you had inconsistency with self.points):
class Route:
def __init__(self):
self.points = []
def add (self, x, y, index):
self.points.insert(Point(x,y), index)

Categories