Writing and using your own functions - basics - python

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")

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")

A few minor remarks:
You should remove the duplicate 8th month (August is listed for 30 and 31 days),
Better replace brackets {} with the list [],
Replace none with None (Python is case-sensitive, None is the keyword),
Add one more condition for a leap year:
(year % 400 == 0) and (year % 100 == 0) -> return True
(year % 4 == 0) and (year % 100 != 0) -> return True

Related

Conditional statement shortcut (python)

I cannot find where my professor taught us to do this. For a conditional statement, if there are multiple numbers ("if x = 1 or 3 or 5 or 9 or ......), how do I make it shorter so I dont have to type every single one? I thought it was the in function???
this is what exactly im trying to do:
elif month == 1 and day == in[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
24, 25, 26, 27, 28, 29, 30]:
print("January", day + "th", year, "is a valid date")
You could iterate through a list and check each time if one of them is equal to day:
days = [4,5,6,7,8,9,10...] #And so on.
for i in days:
if month == 1 and day == i:
print("January", i, "th", year, "is a valid date")
break
If you want to check if variable is in range from x to y, than you can write this code:
if day in range(x, y + 1):
# some code
Note that we add 1 to 2nd argument to range function because range covers numbers from 1st argument to 2nd argument - 1

Need help in finding the day of given (year,month,date) in python

def isYearLeap(year): # Leap year formula
if year % 4 == 0 and year % 100 != 0 :
return True
elif year % 400 == 0 :
return True
else :
return False
testData = [1900, 2000, 2016, 1987] # Test Data as reference
testResults = [False, True, True, False]
for i in range(len(testData)):
yr = testData[i]
print(yr,"->",end="")
result = isYearLeap(yr)
if result == testResults[i]:
print("OK")
else:
print("Failed")
def daysInMonth(year, month): # Finding out days in months in common & leap year
days = 0
mo31 = [1,3,7,5,8,10,12]
if year % 4 == 0 :
days = 1
if year % 100 == 0 :
days = 0
if year % 400 == 0 :
days = 1
if month == 2 :
return 28 + days
if month in mo31 :
return 31
return 30
testYears = [1900, 2000, 2016, 1987] # Test Data as reference
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 dayOfYear(year, month, day):
doy = ["Sat","Sun","Mon","Tue","Wed","Thu","Fri"] # Days of week
monthvalue = [1,4,4,0,2,5,0,3,6,1,4,6] # Months value zellers rule
century = [1700,1800,1900,2000] # Zellers rule
value = [4,2,0,6] # Zellers rule
rem = 0 # Remainder Variable
r = [] # Empty List to compare remainder and to a doy
y = str(year) # Converting year into string
y = int(y[2:4]) # Taking last two digits of string & if used return the function ends
y = int(year) # here returning last two digits
m = int(month)
d = int(day)
mo = [] # Empty list for comparing month with monthly values
dd = 0
if dd == 0 :
for i in range(len(monthvalue)) :
mo.append(i) # Creating mo list
if m in mo:
mo[i] == monthvalue[i]
dd = y // 4 + d + m
if m >= 2 :
dd -= 1
dd += y
for i in range(len(value)) :
if y in century :
y = century[i] == value[i]
dd += y
rem = dd % 7
for i in range(len(doy)) :
r.append(i) # Creating r list
if rem in r :
rem = r[i] == doy[i]
print(rem)
print(dayOfYear(2000, 12, 31)) # Giving output False "\n None
Your code contains a lot of illogical statements and redundancies, that make the code hard to read/understand.
I'd recommend first looking into a few style guides or more basic python tutorials.
Some examples:
y = str(year) # Converting year into string
y = int(y[2:4]) # Taking last two digits of string & if used return the function ends
y = int(year) # here returning last two digits
While the first two statements don't have any effect, their comments makes me think you forgot about two lines of code in between.
dd = 0
if dd == 0 :
...
That if check will never evaluate to false, so it's redundant
for i in range(len(monthvalue)) :
mo.append(i) # Creating mo list
if m in mo:
mo[i] == monthvalue[i]
The mo variable/list is never used again
Please check your program flow and report back
I recognize this as a lab in the netacad cisco python course.
You were tasked with creating an is_year_leap() function in LAB 4.3.1.6, a days_in_month() function in LAB 4.3.1.7, and a day_of_year() function in LAB 4.3.1.8. The functions have one, two, three parameters respectively. Please see below code. Note that I excluded a code to return None if any of the arguments are invalid. I would like you to add that on your own as learning.
# from LAB 4.3.1.6
def is_year_leap(year):
if year % 4 == 0: # leap year occur every 4 years & is divisible by 4
return True
else:
return False
# from LAB 4.3.1.7
def days_in_month(year, month):
# Create list with month days for each month as advised in the LAB
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if is_year_leap(year):
month_days[1] = 29 # 29 days for February on a leap year.
return month_days[month - 1] # month 1 corresponds with index 0 & so on.
# for LAB 4.3.1.8
def day_of_year(year, month, day):
total = 0 # initializing the total variable to add results
# create loop to add only days in the months before the month in the input/test data
for i in range(1, month):
result = days_in_month(year, i)
total += result
day_num = total + day # add the value of the day argument to get day of the year
return day_num
# test data
print(day_of_year(2000, 12, 31))
print(day_of_year(1999, 12, 31))
print(day_of_year(2021, 7, 29))
A point to note is the occurrence of days above the number of days of months.
def is_year_leap(year):
return year % 4 == 0 and not 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 is_year_leap(year):
days[1] = 29
return days[month - 1]
def day_of_year(year, month, day):
if day <= days_in_month(year, month):
count = 0
for yr in range(1, month):
count += days_in_month(year, yr)
return count + day
The leap year should fulfil all its conditions for getting the day of the year.
if the year is divisible by 400 then is_leap_year,
else if the year is divisible by 100 then not_leap_year
else if the year is divisible by 4 then is_leap_year
else not_leap_year
The number of days should also be checked whether it exceeds the day in the month.
#Check for leap year
def is_year_leap(year):
if year%400==0 or (year%100 != 0 and year%4==0):
return True
else:
return False
#Check for number of days in month
def days_in_month(year, month):
month31=[1,3,5,7,8,10,12]
if month in month31:
return 31
elif(month == 2):
if(is_year_leap(year)):
return 29
else:
return 28
else:
return 30
#Check for corresponding day of the year
def day_of_year(year, month, day):
total = 0
if day <= days_in_month(year, month):
total = day
for i in range(month-1):
total += days_in_month(year, i)
return total
else:
return "Your month doesn't have that many days."
print(day_of_year(2000, 12, 31))
print(day_of_year(2001, 12, 31))
print(day_of_year(2008, 12, 31))
print(day_of_year(2000, 11, 31))

Dummy Encoding for Date(time) in Python 3.7

i wanted to create a function that encodes the month of a date(time) object into a season. So for example, if we have 2 seasons and the fourth month the function would return 0 as encoding. My question is now how to create such a function that it is most efficient but also pythonic (less lines of code). I came up with 3 different approaches, the first one looks like this:
def seasonal_encoding(month: int, seasons):
assert 12 % seasons == 0, "Seasons must be in [1, 2, 3, 4, 6, 12]"
if seasons == 1:
return _one_season()
elif seasons == 2:
return _two_seasons(month)
elif seasons == 3:
return _three_seasons(month)
elif seasons == 4:
return _four_seasons(month)
elif seasons == 6:
return _six_seasons(month)
elif seasons == 12:
return _twelve_seasons(month)
where for example three_seasons looks like this:
def _three_seasons(month):
if month in range(1, 5):
return 0
elif month in range(5, 9):
return 1
elif month in range(9, 13):
return 2
This is so far the fastest approach i came up with, but as you can imagine this may work for
a season encoding, but if we want to encode other date(time) attributes, for example hours, this becomes cumbersome real fast. So i came up with a second solution which is a little slower but has less lines of code:
def for_loop_seasonal_encoding(month: int, seasons: int):
assert 12 % seasons == 0, "Seasons must be in [1, 2, 3, 4, 6, 12]"
if seasons != 12:
stepsize = int(12 / seasons)
for i in range(1, seasons+1):
if month in range((i-1) * stepsize + 1, i * stepsize + 1):
return i-1
else:
return month
I also had another solution with numpy before coming up with the for loop solution which i will add for reasons of completeness, but this was four times slower then the other solutions.
def numpy_seasonal_encoding(month: int, seasons):
assert 12 % seasons == 0, "Seasons must be in [1, 2, 3, 4, 6, 12]"
arr = np.array(range(1, 13))
reshaped_arr = arr.reshape(seasons, int(12 / seasons))
return np.where(reshaped_arr == month)[0][0]
So my Question would be: Has anyone of you an idea how to make this as fast as the if representation but with way less lines of codes in "pure" python (so without resorting to Cython, numba etc., external packages like numpy are ok)?
This is the link to the .py file
Python File that includes the function used for timing and the code. Thank you in advance for any help.

Seemingly strange behavior with python counter program

A portion of a recent assignment was to design a program that counts days between dates. I have yet to finish, and I know this could be improved drastically. However, my question is: when I run this program with date2 (below), an error occurs, but this runs as planned with date1 (again, below). I find this behavior strange. These dates are only one day apart and yet one fails and one doesn't. I've tried this with multiple dates, but each fails after 994 days regardless of month, year, day, whatever. Why?
clarification: I define "fail" as
File "first.py", line 35, in counter
return counter(date_array, x+1)
.
def date_test(date_array):
first = (date_array[0], date_array[1], date_array[2])
second = (date_array[3], date_array[4], date_array[5])
if first > second:
return False
elif first != second:
return True
elif first == second:
return False
else:
return "Error: This shouldn't happen."
def counter(date_array, x = 0):
months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
while(date_test(date_array)):
if(date_array[2] == 31 and date_array[1] == 12):
date_array[0] += 1; date_array[1], date_array[2] = 1, 1
return counter(date_array, x+1)
else:
if date_array[2] < months[date_array[1]-1]:
date_array[2] += 1
return counter(date_array, x+1)
else:
date_array[1] += 1; date_array[2] = 1;
return counter(date_array, x+1);
return x
def tuple_test(date):
date_array = []
for x in date:
date_array.append(x)
if not date_test(date_array):
return "The first date is before the second. Swap them."
return counter(date_array)
date1 = (1900,1,1,1902,9,22)
date2 = (1900,1,1,1902,9,23)
print tuple_test(date1)
print tuple_test(date2)
The reason for the error is that you've exceeded your maximum recursion depth.
To "hack" (and verify) this problem you could simply add
import sys
sys.setrecursionlimit(10000)
to the top of your code
you should use the tools python provides
>>> import datetime
>>> date1 = (1900,1,1,1902,9,22) #your weird list with 2 dates
>>> dt1 = datetime.date(*date1[:3]) #create a date object == datetime.date(1900,1,1)
>>> dt2 = datetime.date(*date1[3:]) #create a date object == datetime.date(1902,9,22)
>>> if dt1 < dt2: dt1,dt2 = dt2,dt1 #if dt1 is smaller than dt2, swap them
...
>>> print (dt1 - dt2).days #subtract them and print their days
994
>>>

Printing during different times of week

Welp, I'm a noob when it comes to Python. No doubt about it. Have done some VBS and VB so I have a bit of understanding.
What I am tasked to do using Python seems easy: run an action only during these times:
Mon: between 1:30 am and 7:30 am
Tues – Fri: between 3:00 am 7:30 am
Sat: between 1:00 am and 9:00 am and 5:00 pm to Midnight
Sun: Midnight to 8:30 am
Trouble is, all I've been able to come up with is this (and I'm not even sure this is working properly):
import time
def IsOffHour(time):
if (time.tm_wday > 4):
return True
elif (time.tm_hour >= 17):
return True
elif (time.tm_hour < 8):
return True
else:
return False
now = time.localtime()
if IsOffHour(now):
print 'hello cruel world !'
I'm not sure how to handle the times that start at :30. It's been a bit hard to test, maybe I can change the system date and dime to test it out.
It seems like I'm close, open to ideas.
Thanks!
Instead of using the time module you should try the datetime module. It's much easier for tasks like these.
If you use a fictional date (or replace the date in your checks) than you can do it like this:
>>> x = datetime.datetime(1, 1, 1, 13, 37, 40)
>>> a = datetime.datetime(1, 1, 1, 1, 30, 0)
>>> b = datetime.datetime(1, 1, 1, 7, 30, 0)
>>> a < x < b
False
>>> x = datetime.datetime(1, 1, 1, 5, 0, 0)
>>> a < x < b
True
My ideas:
do check for each day separately (if time_wday == ... or if time_wday in [...])
for checking hours convert them to 24h based string (there is strftime()) and then compare as strings, so instead of time.tm_hour >= .. this will look as hrstr > '13:30' and hrstr < '19:30'
this gives code like:
def IsOffHour(dt):
hrstr = '%02d:%02d' % (dt.tm_hour, dt.tm_min)
if dt.tm_wday == 0:
return '01:30' <= hrstr <= '07:30'
if dt.tm_wday in [1, 2, 3, 4]:
return '03:00' <= hrstr <= '07:30'
if dt.tm_wday == 5:
return '01:00' <= hrstr <= '09:00' or hrstr >= '17:00'
if dt.tm_wday == 6:
return hrstr <= '08:30'
return False
What you should be doing is comparing time objects to time objects rather than extracting the hours and minutes and doing this by hand.
So define the acceptable time windows in your script using time objects and then just see if the current time falls in any of the those windows.
from datetime import datetime,time
# Set our allowed time windows in a dictionay indexed by day, with 0 =
# Monday, 1 = Tuesday etc. Each value is list of tuples, the tuple
# containing the start and end time of each window in that day
off_windows = {
0: [(time(1,30),time(7,30))],
1: [(time(3,0),time(7,30))],
2: [(time(3,0),time(7,30))],
3: [(time(3,0),time(7,30))],
4: [(time(3,0),time(7,30))],
5: [(time(1,0),time(9,0)),(time(16,0),time.max)], #time.max is just before midnight
6: [(time(0,0),time(8,30))]
}
def is_off_hours():
# Get current datetime
current = datetime.now()
# get day of week and time
current_time = current.time()
current_day = current.weekday()
# see if the time falls in any of the windows for today
return any(start <= current_time <= end for (start,end) in off_windows[current_day])
if is_off_hours():
print 'Hello cruel world!'
Above we use the any function which returns True if any value of an iterable is True. So he code loops through the off windows which we have defined for a day, returning true if the current time falls within any of them.
A nice this about python is we can say:
start <= current_time <= end
instead of
start <= current_time and current_time <= end

Categories