This worksheet deviates from the original plan in order to account for the lost week of lecture time during the UICUF strike (Jan 17-22, 2023).
It includes some material on object-oriented programming, based on the discussion from the end of our Python tour (lecture 3) and the material on operator overloading in lecture 4.
Some of you will complete this worksheet after lecture 5, but that material will be covered on worksheet 4. In general, each worksheet after this one will focus on the previous week's lecture material. Usually that will mean 3 lectures of material is available for exploration on a worksheet.
These things might be helpful while working on the problems. Remember that for worksheets, we don't strictly limit what resources you can consult, so these are only suggestions.
plane
module¶First, download the plane.py
module we wrote in lecture and save it somewhere you can find in the terminal and in VS code:
It has a couple of features we didn't discuss in lecture, such as scalar multiplication, e.g.
v = plane.Vector2(1,5)
v*10 # test out Vector2.__mul__
and the reflected version, where the scalar comes before the vector but the vector object still handles the computation:
5*v # test out Vector2.__rmul__
I've also added unary plus and minus for vectors:
-v # negates all components
+v # same as v
Finally, there is now a method __abs__
that makes it so abs(v)
returns a float which is the length of the vector v
. It's natural to use abs
for this since in mathematics, both the length of a vector and the absolute value of a real number are referred to as the "magnitude" of the corresponding object.
abs(v)
This means you can find the distance between two points using abs(P-Q)
!
# Distance from (1,2) to (4,6) should be 5
abs( plane.Point2(1,2) - plane.Point2(4,6) )
However, some things are missing, and this problem asks you to add them.
At the moment, vectors support addition but not subtraction. Fix that. The difference of two vectors u-v
should give a new vector with the property that (u-v)+v
is equal to u
.
v-v # subtraction doesn't work yet
The special method __bool__
decides whether an object evaluates to True
or False
if used in a place where a boolean is expected (e.g. if A:
is equivalent to if A.__bool__():
).
For numbers in Python, zero converts to False and all other numbers convert to True.
It would be natural to make Vector2
objects behave similarly, where the zero vector (with components 0,0
) evaluates to False
and all other vectors evaluate to True
.
Add a __bool__
method to the Vector2
class for this purpose. You can find more info about the __bool__
method at https://docs.python.org/3/reference/datamodel.html#object.__bool__
If v
is a vector or point, we can get the x component with v.x
. In some cases, it might be natural to also treat the vector or point like a list and retrieve the x component with v[0]
. Similarly, we'd want v[1]
to give the y component.
Thankfully, Python translates v[i]
into the method call v.__getitem__(i)
, so this is possible! Write a __getitem__
method for the Vector2
and Point2
classes so that index 0 gives the x component, index 1 gives the y component, and any other index raises the same type of error (IndexError
) you get when you ask for an invalid index from a list:
Antistr
¶In Physics, antimatter refers to a type of matter composed of particles that are "opposite" of the ones that make up the majority of the matter around us. For example, there are antiprotons, antielectrons (positrons), etc..
When a particle collides with its corresponding antiparticle, the two particles annihilate and a huge amount of energy is released. (For this reason, keeping any amount of antimatter around is both dangerous and difficult.)
Make a class Antistr
that behaves like an "antimatter" to ordinary Python strings: An Antistr
can be created from a string, but then represents the sequence of "anticharacters" of all the characters in the string.
Adding strings is like putting matter together. Usually, when you add two Python strings you just get a longer string obtained by joining the two strings together:
"van" + "illa"
But if you add a Python string to the corresponding antistring (Antistr
), they should annihilate and release energy. This should correspond to the message "BOOM!" being printed on the terminal, and an empty string being returned:
Antistr("quail") + "quail" # prints a message about energy release, then returns empty str
"shark" + Antistr("shark")
More generally, it should be possible for an antistring to annihilate just part of a string, as long as the string ends with or begins with the same characters as are in the antistring:
"carpet" + Antistr("pet") # anti-pet annihilates pet, leaving just car
Antistr("un") + "uninspired"
However, if any anticharacters are left over in this process (don't annihilate identical characters from the string), then an exception should be raised.
# time and anti-time annihilate, but antistring " for a snack" is left
# and that is an error
"extensive downtime" + Antistr("time for a snack")
To do this, you'll need to have suitable __add__
and __radd__
methods in your Antistr
class, as well as a constructor that accepts a string.
This exercise is about making a class that can change state depending on a specified pattern of responses to external input. It's not about operator overloading.
Imagine a simplified thermostat that controls heating and cooling of a hotel room. The physical interface might look like this:
It has three buttons: "up"
, "down"
, and "mode"
. It keeps track of the user's desired temperature, the room temperature (which isn't shown), and it can turn two devices on or off: a heater and an air conditioner.
Pressing "up"
increases the desired temperature by one degree, unless the system is in "off" mode in which case it does nothing.
Pressing "down"
increases the desired temperature by one degree, unless the system is in "off" mode in which case it does nothing.
Pressing "mode"
button cycles between operating modes in this order: heat -> cool -> auto -> off (after which it repeats the cycle, going back to heat).
When the system is in heat, cool, or auto mode, it shows the desired temperature on its display panel. But in off mode, it knows the most recently set desired temperature, but does not show it on the display.
The mode, desired temp, and room temp determine what the thermostat does as follows:
Write a class Thermostat
with the following attributes and methods that can be used to simulate this system:
ac_is_on
- attribute, a boolean, always indicating whether the AC is turned onheat_is_on
- attribute, a boolean, always indicating whether the heater is turned on__init__(self)
- method, initializes a new thermostat in which the mode is "off"
, and both the room and the desired temperature are 68
.room_temp(self,x)
- method, tells the thermostat that the room temperature is x
and have it react accordingly.press(self,btn)
- method, simulates the press of a button; the value of btn
should be one of "up"
, "down"
, or "mode"
.get_display(self)
- method, retrieves the text currently displayed on the control panel, in one of these formats:"72/cool"
in cool mode (with 72 being the desired temp)"65/heat"
in heat mode (with 65 being the desired temp)"70/auto"
in auto mode (with 70 being the desired temp)"--/off"
in off mode__str__(self)
and __repr__(self)
- methods that return the same thing, a string in this format:
Thermostat(mode="off",display="--/off",room=68,desired=68,ac_is_on=False,heat_is_on=False)
Hints:
Below is an example of using the class, with commentary.
T = Thermostat()
print(T) # Shows initial state
T.press("up") # In "off" mode, pressing up does nothing. Still set to 68
print(T)
T.press("mode") # Cycle from "off" to "heat" mode
# Room still at desired temp, so heater and AC are both off
print(T)
print(T.get_display()) # Show just what would be on the display
T.room_temp(67) # Temp now too low, so the heater will turn on
print(T)
T.press("down") # Desired temp goes down, now equal to room temp, so heater off
print(T)
T.press("mode") # Cycle from "heat" to "cool" mode. Heater and AC off.
print(T)
T.press("mode") # Cycle from "cool" to "auto" mode. Heater and AC off.
print(T)
T.room_temp(72) # Room temp is now too high, AC will turn on.
print(T)
T.room_temp(59) # Room temp is now too low, heater will turn on.
print(T)
# Repeatedly lower the desired temp while in auto mode
# For a while, heat will stay on.
# Then the room temp and desired temp will be equal, and both heat and AC will be off
# Then the room temp will be higher than the desired temp, and the AC will turn on
for i in range(10):
T.press("down")
print(T)