Writing and using your own functions - basics

Viewed 1985

Your task is to write and test a function which takes two arguments (a year and a month) and returns the number of days for the given month/year pair (yes, we know that only February is sensitive to the year value, but we want our function to be universal). Now, convince the function to return None if its arguments don't make sense.

Use a list filled with the months' lengths. You can create it inside the function - this trick will significantly shorten the code.

I have got the code down but not the 'none' part. Can someone help me with this?

def IsYearLeap(year):
    if (year%4==0):
        return True
    if (year%4!=0):
        return False

def DaysInMonth(year,month):
    if month in {1, 3, 5, 7, 8, 10, 12}:
        return 31

    elif month==2:
        if IsYearLeap(year):
            return 29
        else:
            return 28
    elif month in {4,6,8,9,11}:
        return 30
    else:
        return none

 testyears = [1900, 2000, 2016, 1987,2019]
 testmonths = [ 2, 2, 1, 11,4]
 testresults = [28, 29, 31, 30,33]
 for i in range(len(testyears)):
    yr = testyears[i]
    mo = testmonths[i]
    print(yr,mo,"->",end="")
    result = DaysInMonth(yr,mo)
    if result == testresults[i]:
        print("OK")
    else:
        print("Failed")
6 Answers

It seems that you have rather made a simple mistake. If you are not used the case-sensitive programming languages or have no experience in programming languages, this is understandable.

The keyword None is being misspelled as the undefined word none.

I think your testresults is wrong. February of 1900 should be 29 days also April of 2019 30 days. Also its None instead none. Another things also its better to using list on months list so you could using [1, 3, 5, 7, ...] instead {1, 3, 5, 7, ...}.

Also from your test cases you won't got None, in case you want check this case you could check with month = 13, and you will cover this case

As a further comment to the other good answers to this question, the correct rule for leap years should be something like:

def is_leap_year(year):
    """ is it a leap year?
    >>> is_leap_year(1984)
    True

    >>> is_leap_year(1985)
    False

    >>> is_leap_year(1900)
    False

    >>> is_leap_year(2000)
    True
    """
    return (year % 4 == 0 and 
            (year % 100 != 0 or year % 400 == 0))

Similarly, the test cases need to be clear that 1900 was not a leap year, 2000 was. I recommend writing a separate set of test cases for is_leap_year. Ultimately, in production code, you will be better off to use one of the many time/date libraries. The comments that I've provided make use of doctest to provide this unit test quickly.

A function which does not explicitly return anything implicitly returns None.

In addition to the spelling error (none vs None) you are using this by accident here:

def IsYearLeap(year):
    if (year%4==0):
        return True
    if (year%4!=0):
        return False

Can you see what will happen if neither of the conditions is true? It won't return either False or True, which presumably the caller expects. (Though if you check whether None == True you will get False, and not None is True, so you won't get a syntax error, just a result which might be different from what you expect - the worst kind of bug!)

def IsYearLeap(year):
    return year % 4 == 0 & (year % 400 == 0 | year % 100 != 0)
def DaysInMonth(year,month):
     if month in [1, 3, 5, 7, 8, 10, 12]:
        return 31
     elif month==2:
        if IsYearLeap(year):
            return 29
        else:
            return 28
     elif month in [4,6,8,9,11]:
        return 30
     else:
        return None
#

testYears = [1900, 2000, 2016, 1987]
testMonths = [2, 2, 1, 11]
testResults = [28, 29, 31, 30]
for i in range(len(testYears)):
    yr = testYears[i]
    mo = testMonths[i]
    print(yr, mo, "->", end="")
    result = DaysInMonth(yr, mo)
    if result == testResults[i]:
        print("OK")
    else:
        print("Failed")
def is_year_leap(year):
    return year % 4 == 0 and year % 100 != 0 or year % 400 == 0


def days_in_month(year, month):
    days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    if type(year) != int or year < 1582 or\
    type(month) != int or month < 1 or month > 12:
        return None
    elif is_year_leap(year):
        del days[1]
        days.insert(1, 29)
    return days[month - 1]


test_years = [1900, 2000, 2016, 1987]
test_months = [2, 2, 1, 11]
test_results = [28, 29, 31, 30]
for i in range(len(test_years)):
    yr = test_years[i]
    mo = test_months[i]
    print(yr, mo, "->", end="")
    result = days_in_month(yr, mo)
    if result == test_results[i]:
        print("OK")
    else:
        print("Failed")
Related