.py
files containing your work.This homework assignment must be submitted in Gradescope by Noon central time on Tuesday 31 January 2023.
Collaboration is prohibited, and you may only access resources (books, online, etc.) listed below.
This assignment is about object-oriented programming, based on worksheets 1-3 and lectures 1-4. It focuses on the material of worksheet 3.
The course materials you may refer to for this homework are:
This homework assignment has 2 problems, numbered 2 and 3. The grading breakdown is:
Points | Item |
---|---|
3 | Autograder |
6 | Problem 2 |
6 | Problem 3 |
15 | Total |
The part marked "autograder" reflects points assigned to your submission based on some simple automated checks for Python syntax, etc. The result of these checks is shown immediately after you submit.
Ask your instructor or TA a question by email, in office hours, or on discord.
In Gradescope, the score assigned to your homework submission by the autograder (checking for syntax and docstrings) will be recorded as "Problem 1". Therefore, the numbering of the actual problems begins with 2.
This will happen on every assignment this semester. Starting on the next homework, I won't comment on this practice in the homework, and will just omit problem 1.
Put the work you do for this problem in a file hwk3prob2.py
.
Make a Python class UniversalMimic
whose constructor takes no arguments (e.g. you can make one with UniversalMimic()
). Use operator overloading to make it so that any instance of this class thinks it is equal to every other object. For example:
U = UniversalMimic()
1 == U
U == 0
U == None
U == "a slice of apple pie"
U == 2.75
U == { "course": "MCS 275", "meeting time": "12:00pm", "enrollment_max": 28 }
Note that in an expression like A == UniversalMimic()
, it's possible that A
might have its own __eq__
method that returns False
, and since Python checks with the left object first, you can't avoid this test returning False
. Don't worry about that. Just make your best effort to ensure all equality comparisons this class controls will return True
.
Remark: This is a contrived test of overloading, and not something you should do in actual code.
class UniversalMimic:
"The UniversalMimic class identifies as being equal to all other objects"
# Note that no constructor is required as all objects inherit the no-argument constructor from the object class
def __eq__(self, other):
"Returns true for all objects"
return True
Put the work you do for this problem in a file hwk3prob3.py
.
In lecture 4, we built a module plane.py
that represents
Point2
class, andVector2
classTake that module and modify it (renaming it to hwk3prob3.py
) so that it instead contains these two classes:
Point3
representing three-dimensional points $(x,y,z)$ andVector3
representing three-dimensional vectors $\langle x,y,z \rangle$.Mostly this will involve small changes to account for the third coordinate/component.
But also add one new method to Vector3
:
def cross(self,other):
: Returns the vector cross product $(\mathrm{self}) \times (\mathrm{other})$. Thus if v
and w
are Vector3
objects, you can compute the cross product (which is another Vector3
) as v.cross(w)
.Also take care to update __abs__
so it knows the right way to compute length in 3 dimensions.
No prerequisite course of MCS 275 has 3-dimensional geometry in its required topic list, so here are the things you need to know in this problem:
If anything about the geometric side of this question is unclear, please ask! We mean for it to be a question about translating those ideas into code.
class Point3:
"3D Point representation in the plane"
def __init__(self, x, y, z):
"Initialize new point from x, y, and z coordinates"
self.x = x
self.y = y
self.z = z
def __eq__(self, other):
"points are equal if and only if they have same coordinates"
if isinstance(other, Point3):
return (self.x == other.x) and (self.y == other.y) and (self.z == other.z)
else:
return False
def __add__(self, other):
"point3 + vector3 addition"
if isinstance(other, Vector3):
#add Point3 to another Vector3
return Point3(self.x + other.x, self.y + other.y, self.z + other.z)
else:
# dont allow Point3 addition to other objects
return NotImplemented
def __sub__(self, other):
"point-point subtraction, gives displacement vector"
if isinstance(other, Point3):
return Vector3(self.x - other.x, self.y - other.y, self.z - other.z)
else:
return NotImplemented
def __str__(self):
"human-readable string representation"
return "Point3({},{},{})".format(self.x, self.y, self.z)
def __repr__(self):
"unambiguous string representation"
return str(self)
def distance_to(self, other):
"get distance between two points"
if isinstance(other, Point3):
return abs(self - other)
else:
raise TypeError("A Point3 object is needed to compute the distance")
class Vector3:
"Displacement 3D vector in the plane"
def __init__(self, x, y, z):
"Initialize new vector from x, y, and z components"
self.x = x
self.y = y
self.z = z
def __eq__(self, other):
"vectors are equal if and only if they have same components"
if isinstance(other, Vector3):
return (self.x == other.x) and (self.y == other.y) and (self.z == other.z)
else:
return False
def __add__(self, other):
"vector addition"
if isinstance(other, Vector3):
# vector+vector = vector
return Vector3(self.x + other.x, self.y + other.y, self.z + other.z)
elif isinstance(other, Point3):
# vector + point = point
return Point3(self.x + other.x, self.y + other.y, self.z + other.z)
else:
# vector + anything else = nonsense
return NotImplemented # return this to forbid the requested operation
def __mul__(self, other):
"vector-scalar multiplication"
if isinstance(other, (float, int)): # isinstance allows a tuple of types
# vector*scalar is vector
return Vector3(self.x * other, self.y * other, self.z * other)
else:
return NotImplemented
def __rmul__(self, other):
"scalar-vector multiplication"
# Called if other*self already attempted but failed
# for example if other is an int or float and self is a Vector3
# This "second chance" reflected version of multiplication lets the
# right hand operand decide what to do. In this case, we just decide
# that other*self is the same as self*other (handled by Vector3.__mul__ above)
return self * other
def __neg__(self):
"unary minus"
return Vector3(-self.x, -self.y, -self.z)
def __pos__(self):
"unary plus: return a copy of the object"
return self
def __abs__(self):
"abs means length of a vector"
return (self.x * self.x + self.y * self.y + self.z * self.z) ** 0.5
def __str__(self):
"human-readable string representation"
return "Vector3({},{},{})".format(self.x, self.y, self.z)
def __repr__(self):
"unambiguous string representation"
return str(self)
def cross(self, other):
"computes and returns the cross product of two 3D vectors"
x_cross_comp = self.y * other.z - self.z * other.y
y_cross_comp = self.z * other.x - self.x * other.z
z_cross_comp = self.x * other.y - self.y * other.x
return Vector3(x_cross_comp, y_cross_comp, z_cross_comp)