Lab 6
Lab 6
Solutions must be submitted into Moodle as a single text 3ile, with name lab06.py, into Moodle.
Don’t use an archiver.
Goal of this exercise is that will see dictionaries, class de3initions in Python, and type hints in
action. Additional goal is that you gain some more experience with Python programming.
Make sure that you are using Python version 3.10 or higher, and that you have access to mypy.
Download the starter code lab06.py. It contains the following de3initions:
• An exception class that will be thrown (or more politely ’raised’) when the user tries to
construct an ill-formed Date.
class Date :
year : int
month : int
day : int
1
normalyear = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 )
leapyear = ( 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
The first four lines appear to declare three fields year, month, day, but in fact they are
only type hints. They have no effect at all when the program is run, because the
interpreter merrily ignores them. This applies to local variables, function signatures,
and class variables.
Type checking in Python is done by a separate program called mypy. We expect you to
always use mypy, and annotate variables in your program as much as possible. Type
errors are annoying, but runtime errors are much more annoying.
The field monthnames is a tuple of 12 lists. Each of these lists describes the possible
names that a month can have, for example for January, it is [ ’january’, ’jan’,
1, ’1’ ]. If you want, you can add other languages, for example ’qantar’. The first
entry is always the preferred name, which will be used when the month is printed.
The field monthindex contains a dictionary that maps month names to indices. It is
automatically constructed from monthnames. So we have monthindex[
’january’ ], monthindex[ ’jan’ ], monthindex[ 1 ], and
monthindex[ ’1’ ] all equal to 0.
Field normalyear contains the days in each month in a normal year. Field leapyear
contains the days for each month in a leap year. The only difference between them is
that normalyear[1] = 28, and leapyear[1] = 29.
Tasks:
@staticmethod
def isleapyear( y : int ) -> bool :
that returns True if y is a leap year, and False otherwise. The rule for determining
when a year is a leap year can be easily found on the internet.
Test your implementation carefully, because the remaining tasks rely on it.
2
@classmethod
def __init__( self, year : int, month : Union[ int, str ], day
: int ) :
Note that in Python, constructors are always named __init__ instead of the class
name. (But when you call the constructor, it is still Date.) Another difference is that the
this argument, which is implicit in C++ and Java, must be included in the parameter list.
It must be called self. This applies to all non-static methods of a class. If you want to
refer to a field of a class inside a class method, you also have to write self. in front of
it.
Make sure to use the right order in the remaining parameters: year, month, day (from
big to small.)
The constructor must check that year, month, day are a valid date between 1900 and
2100. If not, it must raise a WrongDate containing an informative message. In order to
raise an WrongDate exception, write for example
Even though the parameters have type declarations, there is nothing that prevents the
user from calling the constructor with ill-typed arguments. Hence, types of parameters
must still be checked at runtime.
@classmethod
def __repr__( self ) -> str :
return "( {}, {}, {} )". format( self. year, self. month,
self. day )
Examples:
Date( ’a’, 4, 5 )
date.WrongDate: year a is not an integer
Date( 1899, ’jan’, 25 )
date.WrongDate: year 1899 is before 1900
3
Date( 1910, 5, 25 )
(1910, 5, 25) # this is a successful construction
Date( 1967, ’wrongember’, 12 )
date.WrongDate: unknown month wrongember
Date( 1910, ’oct’, 25 )
(1910, 10, 25)
Date( 1910, ’jan’, 25.1 )
date.WrongDate: day 25.1 is not an integer
Date( 1919, ’feb’, 29 )
date.WrongDate: month february does not have 29 in year
1919
Date( 1920, ’feb’, 29 )
(1920, 2, 29) # successful construction
We recommend that you add all these cases to the test function below.
3. Python distinguishes between pretty printing (for the user) and ugly print- ing (for the
developer). Ugly printing is done with __repr__( self ), which we already defined.
4. Create a method def weekday( self ) -> str : that returns the day of the
week of the given date.
This can be done by counting the days since January 1st 1900 (it was a Monday), take
this number modulo 7, and index in weekdays.
5. To start your tests, we provided a testing function tester( ). Study the code, and learn
to write such functions by yourself. Careful testing is as important as programming itself.
The function tester( ) consists of two parts. The first part tests the constructor, that
only correct Dates can be constructed, and that appro- priate exceptions are raised when
the arguments do not represent a valid Date.
The second part tests weekday( ), using a mixture of lucky and unlucky dates. Make
sure that all returned weekdays are correct.
Add your own tests, in particular you must add tests for leapyear( ).