I have a function that performs a monthly task on one or more specified days (e.g., the first and the 15th.) For enhanced usability, I want to let users just pass in a single int when they only want it to happen one day each month, or a list of ints for multiple events per month.
monthly_event(days_of_month=1, event="paycheck")
monthly_event(days_of_month=[1,15], event="bills", starting=date(2013,1,1))
Internally the function will iterate over the list and do the same thing it would do for a single int.
Since "int" is not an iterable, I need to do something to avoid a TypeError when the user just passes a single int. I was surprised to find that using an "or" expression and relying on short-circuiting does not work - the TypeError still happens. Here's an example:
from datetime import date as date
dt = date.today()
days = 1
#days = [1,2]
if dt.day == days or dt.day in days:
print "GOOD"
else:
print "BAD"
My first question is: have I misunderstood Python, or does the internal typecheck really happen on the entire line of code before the short-circuiting of the boolean expression? That seems very strange.
My second question is: what is the Pythonic way to do this? I'd like to avoid doing an explicit type check on the "days" variable. Using a try/catch instead just bloats the code:
try:
if dt.day == days:
print "GOOD"
else:
print "BAD"
except TypeError:
if dt.day in days:
print "GOOD"
else:
print "BAD"
Is there something obvious I've overlooked?
days = days if hasattr(days, "__iter__") else [days]import collections; days = days if isinstance(days, collections.Iterable) else [days]days_of_monthto always be an iterable (as its name suggests!). If the caller only wants one day, they can pass in a one-tuple or a list or set with length one or whatever.