Arithmetic with the Result of Python Procedures - python

I have defined a procedure in python which outputs a number, and would like to add the results of calling the procedure on two different inputs. However, when I try to perform arithmetic on the result of a procedure, I am presented with the error message,
TypeError: unsupported operand type(s) for -: 'NoneType' and
'NoneType'.
I tried using the int() function, but apparently this cannot operate on 'NoneType' results. How would I go about adding the two results?
The segment of the code in question is:
def leapYear(year):
if year % 4 != 0:
year = 365
else:
if year % 100 != 0:
year = 366
else:
if year % 400 != 0:
year = 365
else:
year = 366
def daysBetweenDates(year1, month1, day1, year2, month2, day2):
dpY = leapYear(year2) - leapYear(year1)
It's part of my attempted solution for a problem on Udacity (I'm relatively new to coding).

You need to explicitly return the result of the function you wish to use. Therefore, you need to add the following line to the end of your leapYear function:
return year
with a single level of indentation.
Complete example:
def leapYear(year):
if year % 4 != 0:
year = 365
else:
if year % 100 != 0:
year = 366
else:
if year % 400 != 0:
year = 365
else:
year = 366
return year
def daysBetweenDates(year1, month1, day1, year2, month2, day2):
return leapYear(year2) - leapYear(year1)
If a value is not returned explicitly, a Python function returns a None value.
That being said, you can make your life easier by using the datetime module, and in particular the datetime.timedelta objects.

You forgot to return year in leapYear() procedure, by default it will return None.
Use this:
def leapYear(year):
if year % 4 != 0:
year = 365
else:
if year % 100 != 0:
year = 366
else:
if year % 400 != 0:
year = 365
else:
year = 366
return year
def daysBetweenDates(year1, month1, day1, year2, month2, day2):
dpY = leapYear(year2) - leapYear(year1)

Related

How to make a loop that validates the user-input dates based on the functions created?

I am not using any datetime module. I created my own functions to calculate the day, month, and year. I want to calculate the refunds based on the date. If the date is invalid, it should ask the user to try again until a date is true.
year = 0
month = 0
day = 0
money_owed = 0
def if_leap_year(year):
if (year % 400 == 0): return 366
elif (year % 100 == 0): return 365
elif (year % 4 == 0): return 366
else:
return 365
#print(if_leap_year(year))
def days_in_month(month, year):
if month in {1, 3, 5, 7, 8, 10, 12}:
return 31
if month == 2:
if if_leap_year(year):
return 29
return 28
return 30
#print(days_in_month(month, year))
def is_valid_date(year, month, day):
if days_in_month(month, year)<day:#checks if the given day is possible
given the month
return False
else:
return True
def days_left_in_year(month, day, year):
daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31]
daysLeft = (if_leap_year(year) if month < 3 else 365) -
sum(daysInMonth[:month - 1]) - day
return daysLeft
def refund_period():
month = int(input("Enter the month of the year: "))
day = int(input("Enter the day of the year: "))
year = int(input("Enter the year to determine the number of days: "))
if is_valid_date(year , month , day):
money_owed = (days_left_in_year(month, day, year) /
if_leap_year(year)) * 278
return round(money_owed, 2)
else:
print("Your date is invalid, try again.")
while is_valid_date(year, month, day):
print('you will be refunded','$', + refund_period())
break
else:
print("Your date is invalid, try again.")
I am getting:
you will be refunded $ -8.38
even though the calculation shouldn't be performed since the date is invalid
You are setting year =0 , month =0, day = 0 in first loop.
Also the while is not clear. All your functions return an int so never validate if the date is correct.
Maybe you can create a function to validate the date something like this :
def is_valid_date(year , month , day):
if month <1 or month >12: #Validate a allowed month
return False
if day <1 or day > days_in_month(month, year): # validate an allowed day for the month
return False
return True
and you can change this function :
def refund_period():
month = int(input("Enter the month of the year: "))
day = int(input("Enter the day of the year: "))
year = int(input("Enter the year to determine the number of days: "))
if is_valid_date(year , month , day):
money_owed = (days_left_in_year(month, day, year) / if_leap_year(year)) * 278
return round(money_owed, 2)
else :
print("Your date is invalid, try again.")
Just a couple of comments:
You are getting the year, month, and day using input() so you don't need to create global variables for that.
you don't need to ask if if_leap_year(year) == 365 or 366 because this function returns 365 or 366 so you can use it directly when you calculate the money_owned, as I do.
Also you can use if_leap_year(year) instead
(if_leap_year(year) if month < 3 else 365) . That functions return 366 or 365, you dont need to validate again.
And you can use list comprehension for you daysInMonth variable inside days_left_in_year function :
daysInMonth = [days_in_month(m, year) for m in range(1,13)]
Your while loop is not comparing the function value but just checking if the object exists. Instead of conditions like while days_left_in_year(month, day, year), use conditions like while days_left_in_year(month, day, year)<30 (assuming you wanted to deny refunds on orders older than 30 days.
To validate dates, add the following function under your comment #print(days_in_month(month, year)):
def is_valid_date(year, month, day)
if days_in_month(month, year)<day:#checks if the given day is possible given the month
return False
else:
return True
then your condition should look something like this:
if ((is_valid_date(year, month, day) == True) and (month<13)):
print('you will be refunded','$', + refund_period())
else:
print("Your date is invalid, try again.")

Python: logic error in calculating a year (leap)

So I this code which is suppose to return a list with the closest leap year of a list of years.
For example: calling the function with [1995 1750 2018] should return
1996 1748 2016
Which it does for that set of numbers.
The problem I am having is that when a leap year is in the input for example 2008 it does not give me back the closest leap year to 2008. I get back 2008.
Any suggestions as to how I can modify the code to make it work?
code
def is_leap(year):
leap = False
if year % 4 == 0:
if year % 100 != 0 or year % 400 == 0:
leap = True
return leap
major_b = []
major_f = []
newLst = []
def year_forward(yearBounds):
for item in yearBounds:
counter = 0
while not is_leap(item):
item = item + 1
counter += 1
major_f.append([item, counter])
return major_f
def year_backward(yearBounds):
for item in yearBounds:
counter = 0
while not is_leap(item):
item = item - 1
counter -= 1
major_b.append([item,counter])
return major_b
def findLastLeapYears(yearBounds):
forward = year_forward(yearBounds)
backward = year_backward(yearBounds)
counter = 0
for item in forward:
if abs(item[1]) < abs(backward[counter][1]):
newLst.append (str(item[0]))
counter+=1
elif abs(item[1]) == abs(backward[counter][1]):
if item[0] < backward[counter][0]:
newLst.append (str(item[0]))
counter += 1
else:
newLst.append (str(backward[counter][0]))
counter += 1
else:
newLst.append (str(backward[counter][0]))
counter+=1
return newLst
I'd avoid trying to roll your own leap year detection code. Use calendar.isleap to determine whether a year is a leap year or not.
Then go in a loop, like this:
import calendar
def find_nearest_leap(year):
offset = 1
while True:
if calendar.isleap(year - offset):
return year - offset
if calendar.isleap(year + offset):
return year + offset
offset += 1
To find the list of nearest leap years for a list of values, do this:
nearest_leap_years = [find_nearest_leap(year) for year in years]
Where years is the list of years you are interested in.
I'm also assuming the nearest leap year isn't the year itself, which seems to be a constraint of the problem...

Number of days with traffic intensity from tuples in a list [duplicate]

This question already has answers here:
TypeError: '<=' not supported between instances of 'str' and 'int' [duplicate]
(4 answers)
Closed 4 years ago.
I'm trying to write a function that uses another function called traffic_intensity and takes a list of tuples, returning the number of days with the given traffic intensity. This is what I have at the moment:
def traffic_intensity(count):
"""Returns string indicating intensity level given by number of
vehicles"""
level_name = ""
if(count < 5000):
level_name = "Very Low"
elif(count >= 5000 and count < 10000):
level_name = "Low"
elif(count >= 10000 and count < 18000):
level_name = "Moderate"
elif(count >= 18000):
level_name = "High"
return(level_name)
def n_days_at_intensity(vehicle_records, intensity):
"""Returns number of days with the given traffic intensity level"""
days = 0
for number in intensity:
traffic = traffic_intensity(number)
if traffic == intensity:
days+= 1
return days
This is currently giving me an error
But it is supposed to return me an output of
3
for the test code:
vehicle_records = [('2010-01-01',1),
('2010-01-02',2),
('2010-01-03',3)]
days = n_days_at_intensity(vehicle_records, 'Very Low')
print(days)
and an output of
0
for test code
voiture_records = [('2010-01-01',1),
('2010-01-02',2),
('2010-01-03',3)]
days = n_days_at_intensity(voiture_records, 'Moderate')
print(days)
Can someone please tell me how my code can be fixed to get these outputs?
For n_days_at_intensity, the vehicle_records you passed in is a list of tuples. Try looping through the list of tuples as below:
def n_days_at_intensity (vehicle_records, intensity):
"""Returns number of days with the given traffic intensity level"""
days = 0
# loop through list of tuples
for r in vehicle_records:
r_date = r[0]
r_count = r[1]
traffic = traffic_intensity(r_count)
if traffic == intensity:
days += 1
return days
Then calling the below should returns 3:
vehicle_records = [('2010-01-01',1),
('2010-01-02',2),
('2010-01-03',3)]
days = n_days_at_intensity(vehicle_records, 'Very Low')
print(days)

Assert syntax error

Getting syntax error for the following line trying to use assert to test.
#test is_valid_date for April 4, 2014 and Januarary 3, 2012
print assert(is_valid_date(2014,4,4))
print assert(is_valid_date(2012,1,3))
Shouldn't the assert return true for the above if the function is_valid_date returns true?
Here is the actual is_valid_date implementation.
def is_valid_date(year, month, day):
"""
Inputs:
year - an integer representing the year
month - an integer representing the month
day - an integer representing the day
Returns:
True if year-month-day is a valid date and
False otherwise
"""
if year > datetime.MINYEAR and year < datetime.MAXYEAR:
if month >= 1 and month <= 12:
d = days_in_month(year, month)
if day >= 1 and day <= d:
return True
return False
assert is not a function, it's a statement, and as such cannot be used inside the expression for print.
What you probably want to do is this:
is_valid = is_valid_date(2014, 4, 4)
print is_valid
assert is_valid
That is, first execute the print statement, then execute the assert statement (although, for a function returning only True or False there's no much benefit in printing the return value before the assertion).
If you find yourself writing code like that often, you can consider writing your own utility function:
def verbose_assert(value):
print value
assert value
verbose_assert(is_valid_date(2014, 4, 4))
Or even like this:
def assert_is_valid_date(*args):
is_valid = is_valid_date(*args)
print is_valid
assert is_valid
assert_is_valid_date(2014, 4, 4)
What do you expect assert to return and why would you want to print that value? Your usage is just not very idiomatic. Take out the print and add a description of the thing you are testing to the assert as its second argument.
assert is_valid_date(2014,4,4), "2014,4,4 is a valid date tuple"
assert is_valid_date(2012,1,3), "2012,1,3 is a valid date tuple"
Tangentially, maybe you want to refactor your function to avoid the arrow antipattern
def is_valid_date(year, month, day):
"""
Inputs:
year - an integer representing the year
month - an integer representing the month
day - an integer representing the day
Returns:
True if year-month-day is a valid date and
False otherwise
"""
if year <= datetime.MINYEAR:
return False
if year >= datetime.MAXYEAR:
return False
if month < 1:
return False
if month > 12:
return False
if day < 1:
return False
if day > days_in_month(year, month):
return False
return True
This is perhaps overdoing it, but you'll notice how adding new conditions is now easy, and adding debug prints to see exactly where the code is rejecting an input is now very straightforward.

How to find the difference in y-m-d between two dates in 'dd/mm/yy' format? [duplicate]

This question already has answers here:
calculate the difference between two datetime.date() dates in years and months
(3 answers)
Closed 9 years ago.
There are many posts about finding the difference between two dates but the values involved start and end with different formatting than those I am using eg:
a = 01/01/10 # dd/mm/yy format
b = 01/01/05 # dd/mm/yy format
So I am after the difference in years, months and days between a and b where the output required is in the format x years, x months, x days (if required) format.
I'm reading the datetime documentation and have had a crack at figuring it out (note: admittedly newbie code ahead, i was trying to piece together all the demo's there so had to make a few modifications):
from datetime import datetime as dt
# calculate duration between two dates
# later date
later_date = '01/01/10'.replace('/', '')
# reverse the order
later_date = "".join(reversed([later_datet[i:i+2] for i in range(0, len(later_date), 2)]))
# separate with commas every two numbers
later_date = ','.join(later_date[i:i+2] for i in range(0, len(later_date), 2))
# convert to datetime object
later_date = dt.strptime(later_date, "%y,%m,%d")
# earlier date
earlier_date = '01/01/05'.replace('/','')
# reverse the order
earlier_date = "".join(reversed([earlier_date[i:i+2] for i in range(0, len(earlier_date), 2)]))
# separate with commas every two numbers
earlier_date = ','.join(earlier_date[i:i+2] for i in range(0, len(earlier_date), 2))
# convert to datetime object
earlier_date = dt.strptime(earlier_date, "%y,%m,%d")
duration = later date - earlier_date
print duration
print type(duration)
is outputting:
1826 days, 0:00:00
<type 'datetime.timedelta'>
So I think i am somewhat close to getting the correct data, but now i need to convert it into the x years, x months, x days (if required) format.
Edit/Solution:
I have put some code together and am testing now, I think it is working for all date combinations but if anyone notices a bug please let me know:
"""
this code calculates the duration between two dates (a later and earlier date)
in the format dd/mm/yy and returns the duration in years, months and days with
correct formatting in regards to the plurality of the year/s, month/s, and day/s
and the punctuation required dependent on whether one or more values are returned
ie multiple values are separated by ',' whereas a singular value is terminated by '.'.
"""
# imported libraries
from datetime import datetime as dt
from dateutil import relativedelta
import sys
# initial date objects
later_date = '01/01/10'
earlier_date = '01/01/05'
# convert dates to required format
a_date = dt.strptime(later_date, '%d/%m/%y')
b_date = dt.strptime(earlier_date, '%d/%m/%y')
# get duration using dateutil
duration = relativedelta.relativedelta(a_date, b_date)
# check if number of years is not false ie != 0
if duration.years != 0:
years = duration.years
else:
years = False
# check if number of months is not false ie != 0
if duration.months != 0:
months = duration.months
else:
months = False
# check if number of days is not false ie != 0
if duration.days != 0:
days = duration.days
else:
days = False
# add values to a list
date_list = [years,months,days]
# count instances of False in the list
false_count = date_list.count(False)
# iterate over list with enumeration performing value and
# boolean checking to predicate plurality and punctuality
# requirements.
for n, _ in enumerate(date_list):
# year/s - single or plural, lone value or more
if _ != False and n == 0:
single_year = date_list[0] == 1
# if single and not lone
if single_year == True and false_count != 2:
sys.stdout.write(str(_)+' year, ')
# if single and lone
elif single_year == True and false_count == 2:
sys.stdout.write(str(_)+' year.')
# if not single and not lone
elif single_year == False and false_count != 2:
sys.stdout.write(str(_)+' years, ')
# if not single but lone
elif single_year == False and false_count == 2:
sys.stdout.write(str(_)+' years.')
# if there are no years, still provide value for possible later concatenation
if _ == False and n == 0:
datasetduration_y = ''
# month/s - single or plural, lone value or more
if _ != False and n == 1:
single_month = date_list[1] == 1
# if single and not lone
if single_month == True and false_count != 2:
sys.stdout.write(str(_)+' month, ')
# if single and lone
elif single_month == True and false_count == 2:
sys.stdout.write(str(_)+' month.')
# if not single and not lone and there are days
elif single_month == False and false_count != 2 and date_list[2] != False:
sys.stdout.write(str(_)+' months, ')
# if not single and not lone and there are no days
elif single_month == False and false_count != 2 and date_list[2] == False:
sys.stdout.write(str(_)+' months.')
# if not single but lone
elif single_month == False and false_count == 2:
sys.stdout.write(str(_)+' months.')
# if there are no months, still provide value for possible later concatenation
if _ == False and n == 1:
datasetduration_m = ''
# day/s - single or plural, lone value or more
if _ != False and n == 2:
single_day = date_list[2] == 1
# if single and not lone
if single_day == True and false_count != 2:
sys.stdout.write(str(_)+' day.')
# if single and lone
elif single_day == True and false_count == 2:
sys.stdout.write(str(_)+' day.')
# if not single and not lone
elif single_day == False and false_count != 2:
sys.stdout.write(str(_)+' days.')
# if not single but lone
elif single_day == False and false_count == 2:
sys.stdout.write(str(_)+' days.')
# if there are no days, still provide value for possible later concatenation
if _ == False and n == 2:
datasetduration_d = ''
Well, here we go. This is not a datetime-related solution. But, I think this should still get you what you are you asking...
Number of years: 1826/365. See how many whole years have passed.
Number of months: (1826%365)/30. Of the remaining days, how many months. (Here, we are ignoring the specific month lengths (Jan=31, Feb=28, etc) and just using 30days/month).
Number of days: (1826%365)%30. Of the remaining days, how many days.
This should do the trick
>>> from datetime import datetime as dt
>>> a = '01/01/2010'
>>> b = '01/01/2005'
>>> a_date = dt.strptime(a, '%d/%m/%Y') # Use capital Y for year with century
>>> b_date = dt.strptime(b, '%d/%m/%Y')
>>> td = a_date - b_date
>>> td
datetime.timedelta(1826)
You now have a timedelta object that contains the number of days between a and b.
It's not quite clear what it means to express this in years, months and days, since none of "year" or "month" or "day" are a standardized unit of time - years and months have a variable number of days, and days have a variable number of seconds. In this case I think it is best to leave the difference expressed in days, which you can get by doing
>>> td.days
1826
If you really want to express the number of days, years and months, then you could operate as follows
>>> nyears = a_date.year - b_date.year
>>> nmonths = a_date.month - b_data.month
>>> ndays = a_date.day - b_data.day
Which gives you the difference in years, days and months, subject to the proviso that they can be negative. There are a few options for dealing with that. One way is to allow negative differences in number of days, months and years (which makes perfect mathematical sense). Another option is to adjust them:
>>> if ndays < 0:
ndays = ndays + 30
nmonths = nmonths - 1
>>> if nmonths < 0:
nmonths = nmonths + 12
nyears = nyears - 1
A trivial answer:
years, remainder = divmod(duration.days, 365)
months, days = divmod(remainder, 30)
print "{} years, {} months {} days".format(years,months,days)

Categories

Resources