This week we will work with classes representing:
class Point: """Representation of a 2D point.""" def __init__(self, x, y): """Initializes the Point object. Attributes: x (float): x coordinate of the point. y (float): y coordinate of the point. """ self.x = x self.y = y def move(self, dx, dy): return Point(self.x + dx, self.y + dy) def __str__(self): return "Point(x={}, y={})".format(self.x, self.y) def __eq__(self, other): return (self.x == other.x and self.y == other.y)
class Rectangle: """Representation of a rectangle""" def __init__(self, corner, height, width): """Initializes the Rectangle object. Attributes: corner (Point): coordinates of the left upper corner of the rectangle. height (float): height of the rectangle. width (float): width of the rectangle. """ self.corner = corner self.height = height self.width = width def __str__(self): return "Rectangle({}, h={}, w={})".format(str(self.corner), self.height, self.width) def __eq__(self, other): return (self.corner == other.corner and self.height == other.height and self.width == other.width)
If a function/method operating on an object is pure (returns a modified copy of the original object), it is possible to use the dot operator multiple times.
if __name__ == "__main__": p1 = Point(2, 3) print(p1) # dot operator composition p2 = p1.move(1, -2) print(p2) p3 = p1.move(1, 0).move(0, -2) print(p3)
Now, let's try changing the object's attributes via a (global) function:
def move_point(p, dx, dy): p.x += dx p.y += dy
p1 = Point(2, 3) # objects mutable print(p1) move_point(p1, 2, 3) print(p1)
There are several ways to copy an object, each of which copies an object in a slight difference sense:
=
operator: Makes variables to point to the same object in memory
copy.copy()
function: Shallow copy (1 level deep). A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
copy.deepcopy()
function: Deep copy (recursive). A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
There are also 2 ways of comparing 2 objects:
is
and is not
checks for identity: Everything in Python is an object, and each object is stored at a specific memory location. The Python is and is not operators check whether two variables refer to the same object in memory. Note: Keep in mind that objects with the same value are usually stored at separate memory addresses.
==
and !=
checks for equality: Checks whether or not two objects have the same value, regardless of where they’re stored in memory. In the vast majority of cases, this is what you want to do. The magic of the equality operator ==
happens in the __eq__()
class method of the object to the left of the ==
sign. This is a magic class method that’s called whenever an instance of this class is compared against another object. If this method is not implemented, then ==
compares the memory addresses of the two objects by default (also see here). Check our implementation of __eq__()
methods of the Point and Rectangle classes.
1. Copy using the “=” operator. The 2 variables pointing to the exact same object in memory.
if __name__ == "__main__": r1 = Rectangle(Point(2, 3), 10, 8) print("r1:", r1) # copy using the "=" operator # only reference is copied # difference variables pointing to the exact same object r2 = r1 print("r1 == r2: ", r1 == r2) print("r1 is r2: ", r1 is r2) print("\nr2.height = 5") r2.height = 5 print("r1:", r1) print("r2:", r2) print("r1 == r2: ", r1 == r2) print("r1 is r2: ", r1 is r2) print("\nr2.corner.x = 0") r2.corner.x = 0 print("r1:", r1) print("r2:", r2) print("r1 == r2: ", r1 == r2) print("r1 is r2: ", r1 is r2)
r1: Rectangle(Point(x=2, y=3), h=10, w=8) r1 == r2: True r1 is r2: True r2.height = 5 r1: Rectangle(Point(x=2, y=3), h=5, w=8) r2: Rectangle(Point(x=2, y=3), h=5, w=8) r1 == r2: True r1 is r2: True r2.corner.x = 0 r1: Rectangle(Point(x=0, y=3), h=5, w=8) r2: Rectangle(Point(x=0, y=3), h=5, w=8) r1 == r2: True r1 is r2: True
2. Copy using the copy.copy() fuction (shallow copy). Separate copies up to the 1st level.
if __name__ == "__main__": r1 = Rectangle(Point(2, 3), 10, 8) print("r1:", r1) # copy using the copy.copy() function # copy only 1 level deep # called a shallow copy r3 = copy.copy(r1) print("r1 == r3: ", r1 == r3) print("r1 is r3: ", r1 is r3) print("\nr3.height = 5") r3.height = 5 print("r1:", r1) print("r3:", r3) print("r1 == r3: ", r1 == r3) print("r1 is r3: ", r1 is r3) print("\nr3.corner.x = 0") r3.corner.x = 0 print("r1:", r1) print("r3:", r3) print("r1 == r3: ", r1 == r3) print("r1 is r3: ", r1 is r3)
r1: Rectangle(Point(x=2, y=3), h=10, w=8) r1 == r3: True r1 is r3: False r3.height = 5 r1: Rectangle(Point(x=2, y=3), h=10, w=8) r3: Rectangle(Point(x=2, y=3), h=5, w=8) r1 == r3: False r1 is r3: False r3.corner.x = 0 r1: Rectangle(Point(x=0, y=3), h=10, w=8) r3: Rectangle(Point(x=0, y=3), h=5, w=8) r1 == r3: False r1 is r3: False
3. Copy using the copy.deepcopy() function (deep copy). Separate copies including child objects.
if __name__ == "__main__": r1 = Rectangle(Point(2, 3), 10, 8) print("r1:", r1) # copy using the copy.deepcopy() function # copy of the child objects (recursively) # called a deep copy r4 = copy.deepcopy(r1) print("r1 == r4: ", r1 == r4) print("r1 is r4: ", r1 is r4) print("\nr4.height = 5") r4.height = 5 print("r1:", r1) print("r4:", r4) print("r1 == r4: ", r1 == r4) print("r1 is r4: ", r1 is r4) print("\nr4.corner.x = 0") r4.corner.x = 0 print("r1:", r1) print("r4:", r4) print("r1 == r4: ", r1 == r4) print("r1 is r4: ", r1 is r4)
r1: Rectangle(Point(x=2, y=3), h=10, w=8) r1 == r4: True r1 is r4: False r4.height = 5 r1: Rectangle(Point(x=2, y=3), h=10, w=8) r4: Rectangle(Point(x=2, y=3), h=5, w=8) r1 == r4: False r1 is r4: False r4.corner.x = 0 r1: Rectangle(Point(x=2, y=3), h=10, w=8) r4: Rectangle(Point(x=0, y=3), h=5, w=8) r1 == r4: False r1 is r4: FalseDeep copy
Motivation: The +
operator will perform arithmetic addition on two numbers, merge two lists, or concatenate two strings. How should it work with your class?
__str__(self)
: Controls how the object gets printed. Also, this same method is invoked when we use the built-in function str()
or format()
.
__eq__(self, other)
: Equality operator.
__add__(self, other)
: The addition operation +
is carried out the way we specify in this special method.
__lt__(self, other)
: Implementation of the less than symbol <
symbol in our class.
class Point: """Representation of a 2D point.""" def __init__(self, x, y): """Initializes the Point object. Attributes: x (float): x coordinate of the point. y (float): y coordinate of the point. """ self.x = x self.y = y def __add__(self, other): return Point(self.x + other.x, self.y + other.y) def __lt__(self, other): norm_self_quadr = self.x ** 2 + self.y ** 2 norm_other_quadr = other.x ** 2 + other.y ** 2 return norm_self_quadr < norm_other_quadr
Create a class representing a 2D point in polar coordinates. Each such point is characterized by an ordered pair $(r, \phi)$:
Your class must have the following structure. Copy-paste the following code and implement the (special) methods:
Note:
__mul__(self, other)
and __truediv__(self, other)
return an instance of the PointPolar class leaving both original points unchanged.
<
, ⇐
, >
, >=
) two points by their radial distances only.
Required filename: 11_weekly_hw.py
.
class PointPolar: """represents a point in polar coordinates""" def __init__(self, r, phi): """ :param r: float, norm of the point :param phi: float, angular coordinate """ def __str__(self): """ >>> p1 = PointPolar(5.1, 0.2) >>> print(p1) Point: r=5.1, phi=0.2 """ def __eq__(self, other): """Equality given by both parameters""" def __mul__(self, other): """Multiplication of 2 points""" def __truediv__(self, other): """Division of 2 points""" def __lt__(self, other): """Is self < other?""" def __le__(self, other): """Is self <= other?""" def __gt__(self, other): """Is self > other?""" def __ge__(self, other): """Is self >= other?"""