辅导ACIT 2515、讲解Python编程设计、Python程序调试、辅导Inheritance
- 首页 >> Python编程ACIT 2515 – Object Oriented Programming - Lab 5 (Monday Sets)
Inheritance and
Instructor Mike Mulder (mmulder10@bcit.ca)
Also available on Slack.
Total Marks 25
Due Dates Sunday, Feb. 10, 2019 by midnight
You must use your Lab 4 code as your starting point for this lab.
Goals
Apply the Object Oriented Programming Principles of Inheritance and Polymorphism
(and the Template Method design pattern).
To read and develop objects based on UML Class notation and relationships.
Continue to exercise good Python programming practices including naming conventions,
documentation and unit testing.
Overview
Your company has decided to add a line of pressure sensors to their product offering, in
addition to the existing temperature sensors. Since you have refactored the sensor_results.py
script to be more object oriented, you believe to can easily refactor that code again using
inheritance and polymorphism to support both temperature and pressure sensors.
Your approved design is expressed below in UML format. You are now ready to refactor your
code once again to conform to the design.
The reading data for the pressure sensor in the csv file is slightly different than that of the
temperature sensor.
Temperature Sensor:
2018-09-23 19:59:01.873,1,ABC Sensor Temp M301A,20.212,21.641,22.017,OK
2018-09-23 20:00:02.453,2,ABC Sensor Temp M301A,100.000,100.000,100.000,HIGH_TEMP
2018-09-23 20:00:03.563,3,ABC Sensor Temp M301A,-50.000,-50.000,-50.000,LOW_TEMP
Pressure Sensor:
1,2018-09-23 19:59:01.234,ABC Sensor Pres M100,49.512,51.841,55.418,GOOD
2,2018-09-23 20:00:02.452,ABC Sensor Pres M100,100.000,100.000,100.000,HIGH_PRESSURE
3,2018-09-23 20:01:03.876,ABC Sensor Pres M100,0.000,0.000,0.000,LOW_PRESSURE
The sequence number is in a different location and the status values have different values for
pressure sensor readings.
2
You have represented your design with the following UML Class diagram:
AbstractSensor
- sensor_readings : AbstractReading[0..*]
+ get_sensor_name() : string
+ get_time_period() : string[2]
+ get_reading_stats() : ReadingStats
+ get_error_messages() : string[0..*]
- _load_reading_row(row : string[]) : AbstractReading
ReadingStats
- lowest_reading : float
- average_reading : float
- highest_reading : float
- largest_reading_range : float
+ get_lowest_reading() : float
+ get_average_reading() : float
+ get_highest_reading() : float
+ get_largest_reading_range() : float
Notes:
Methods in italics are abstract methods (which must be implemented in a child class).
Items highlighted in yellow show key changes to the design from your previous lab(s).
1 *
AbstractReading
<Your Existing Attributes>
<Your Existing Methods>
+ is_error() : boolean
+ get_error_msg() : string
TemperatureSensor
- _load_reading_row(row : string[]) :
TemperatureReading
PressureSensor
- _load_reading_row(row : string[]) :
PressureReading
TemperatureReading
+ is_error() : boolean
+ get_error_msg() : string
PressureReading
+ is_error() : boolean
+ get_error_msg() : string
3
Instructions
Sensor Readings
Rename the existing SensorReading class to AbstractReading and make abstract
methods for handling the error readings as the temperature and pressure sensor
readings have different status values (is_error and get_error_msg methods).
Create new TemperatureReading (temperature_reading.py) and PressureReading
(pressure_reading.py) classes that are child classes of the AbstractReading parent class.
They will provide concrete implementations of the is_error and get_error_msg
methods.
TemperatureSensor:
o is_error returns False for a status of “OK” and True otherwise.
o get_error_message returns the formatted error message for a temperature
reading (i.e., High Temperature (100°C) at 2018/09/23 20:00, Sequence: 5)
o Define and use constants for the temperature sensor status values
PressureSensor:
o is_error returns False for a status of “GOOD” and True otherwise.
o get_error_message returns the formatted error message for a pressure reading
(i.e., High Pressure (100 kPa) at 2018/09/23 20:00, Sequence: 5)
o Define and use constants for the pressure sensor status values
You do NOT need to submit any unit tests for the AbstactReading, TemperatureReading
or PressureReading classes.
Make sure the attribute and method names in AbstractReading are NOT specific to
temperature or pressure readings. For example, a method called get_avg_temp should
be renamed get_avg_reading. Likewise, a method called get_temp_range should be
renamed as get_reading_range.
Sensor
Rename the existing Sensor class to AbstractSensor with an abstract method for loading
a row representing a single reading from the csv file (_load_reading_row).
AbstractSensor must be updated to use the is_error and get_error_msg methods from
AbstractReading (if you have not already done so in the previous labs).
o is_error – to determine if the reading is an error in AbstractSensor methods
get_reading_stats and get_error_messages
o get_error_msg – to get the error message of a reading in AbstractSensor method
get_error_messages
Create new TemperatorSensor (temperature_sensor.py) and PressureSensor
(pressures_sensor.py) classes that are child classes of the AbstractSensor class. They will
provide concrete implementations of the _load_reading_row method specific to each of
the temperature and pressure sensor csv reading formats.
4
Create unit tests for both the TemperatureSensor (test_temperature_sensor.py) and
PressureSensor (test_pressure_sensor.py) classes using mocked csv data. These unit
tests will be identical to your previous unit test for the Sensor class except:
o TestTemperatureSensor will create instances of TemperatureSensor
o TestPressureSensor will create instances of PressureSensor
o TestPressureSensor will have TEST_READINGS that contain pressure reading data
(see section Pressure Readings Test Data of lab write-up).
Do NOT include a unit test for AbstractSensor.
sensor_results.py
Refactor this script to create both a TemperatureSensor and a PressureSensor object
and generate a report for each.
Make sure to pass the “temperature_results.csv” filename to the constructor for
TemperatureSensor and the “pressure_results.csv” filename to the constructor for
PressureSensor when creating these objects. These csv files have been provided in the
Lab 5 zipfile.
See below for the expected output from sensor_results.py.
Expected Output:
Sensor: ABC Sensor Temp M301A
Period: 2018/09/23 19:56 to 2018/09/23 20:04
Lowest Temp: 20.142000°C
Average Temp: 21.57878°C
Highest Temp: 22.703000°C
Largest Temp Range: 1.940000°C
Error Messages:
High Temperature (100°C) at 2018/09/23 20:00, Sequence: 5
Low Temperature (-50°C) at 2018/09/23 20:04, Sequence: 11
Sensor: ABC Sensor Pres M100
Period: 2018/09/23 19:56 to 2018/09/23 20:06
Lowest Pressure: 50.142000 kPa
Average Pressure: 51.58633 kPa
Highest Pressure: 55.017000 kPa
Largest Pressure Range: 4.805000 kPa
Error Messages:
High Pressure (100 kPa) at 2018/09/23 20:00, Sequence: 5
Low Pressure (0 kPa) at 2018/09/23 20:06, Sequence: 11
Grading Summary
Sensor Readings Implementation
AbstractReading, TemperatureReading and
PressureReading classes (6 marks)
6 marks
Sensor Implementation
AbstractSensor, TemperatureSensor and
PressureSensor classes (9 marks)
13 marks
5
Unit Tests for TemperatureSensor and Pressure
Sensor classes (4 marks)
Integration – sensor_results.py
Refactoring and adding in the new Pressure
Report (4 marks)
Correct report output (2 marks)
6 marks
Marks will be subtracted poor programming practices,
including:
Violations of naming conventions
Missing or invalid DocString
Failing unit tests
Unnecessary print statements left in code
Note: Not applicable to the sensor_results.py script.
-1 mark each
Total 25 marks
Submission
The following files must be submitted as a zipfile called lab5.zip:
sensor_results.py
abstract_reading.py
temperature_reading.py
pressure_reading.py
abstract_sensor.py
temperature_sensor.py
test_temperature_sensor.py
pressure_sensor.py
test_pressure_sensor.py
reading_stats.py
Sufficient code must be submitted to run sensor_results.py otherwise no marks will be received
for the lab.
Pressure Readings Test Data
You may use this as the test data for your unit tests. It matches the readings in
pressure_results.csv.
TEST_READINGS = [
["1", u"2018-09-23 19:56:01.345", "ABC Sensor Pres M100", "50.152", "51.367", "52.005",
"GOOD"],
["2", "2018-09-23 19:57:02.321", "ABC Sensor Pres M100", "50.163", "51.435", "52.103",
"GOOD"],
["3", "2018-09-23 19:58:01.224", "ABC Sensor Pres M100", "50.142", "51.528", "51.803",
"GOOD"],
["4", "2018-09-23 19:59:03.843", "ABC Sensor Pres M100", "50.212", "51.641", "52.017",
"GOOD"],
6
["5", "2018-09-23 20:00:01.143", "ABC Sensor Pres M100", "100", "100", "100",
"HIGH_PRESSURE"],
["6", "2018-09-23 20:01:01.111", "ABC Sensor Pres M100", "51.244", "51.355", "52.103",
"GOOD"],
["7", "2018-09-23 20:02:02.324", "ABC Sensor Pres M100", "51.112", "52.345", "52.703",
"GOOD"],
["8", "2018-09-23 20:03:02.744", "ABC Sensor Pres M100", "50.513", "51.745", "52.105",
"GOOD"],
["9", "2018-09-23 20:04:01.321", "ABC Sensor Pres M100", "50.333", "51.348", "51.943",
"GOOD"],
["10", "2018-09-23 20:05:01.999", "ABC Sensor Pres M100", "50.332", "51.445", "52.013",
"GOOD"],
["11", "2018-09-23 20:06:02.022", "ABC Sensor Pres M100", "0", "0", "0", "LOW_PRESSURE"] ]
Documentation Best Practices
Use the following documentation practices below for this lab.
Class Documentation Add a comment describing what the class represents.
Use DocString as per the following example:
class Point:
"""Represents a point in 2D geometric coordinates"""
def __init__(self, x=0, x=y):
...
Method Documentation Add a comment describing what the method does.
def __init__(self, x=0, x=y):
"""Initialize the position of a new point. The x and y
Coordinates can be specified. If they are not, the
point defaults to the origin. """
def move(self, x, y):
"""Move the point to a new position in 2D space. """
self.x = x
self.y = y
Docstring Reference: https://www.python.org/dev/peps/pep-0257/
Naming Best Practices
Use the following naming practices for this lab.
Class Name CapitalizedWords (aka CamelCase)
Instance Variables lower_case_with_underscores
Note: Use _lower_case_with_underscores for internal (i.e.,
private) instance variables.
Methods lower_case_with_underscores
Note: Use _lower_case_with_underscores for internal (i.e.,
private) methods.
Reference Style Guide: https://www.python.org/dev/peps/pep-0008/