Well my first question is, what's a spec_weekday
? It can apparently be "2nd", "4th" as well as "teenth" or "last". What can't it be? Well it can't be any string besides "teenth" or "last" that doesn't begin with a number. Not even whitespace apparently. None of this is explicitly told to the user, you don't even raise helpful errors. If I tried to do this:
meetup_day(2013, 6, "Sunday", "last Sunday")
I get this unhelpful error:
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
meetup_day(2013, 6, "Sunday", "last Sunday")
File "<pyshell#1>", line 14, in meetup_day
spec_weekday = int(spec_weekday[0:1])
ValueError: invalid literal for int() with base 10: 'l'
I can figure out that 'l' is the first letter of my spec_weekday
, but that requires investigation. Instead, you should have a docstring that explains what can be passed to parameters. Docstrings are programmatically accessible comments that tell users how to interact with your code. It's a good idea to have one in most functions, especially if the execution and arguments are non-obvious.
def meetup_day(year, month, weekday, spec_weekday):
"""Returns date object from a wordy description of a date
Eg. "Last Sunday in November 2013" could be entered with:
meetup_day(2013, 11, "Sunday", "last")
spec_weekday can be an ordinal number, like 1st, 2nd, 3rd etc.
or it can be "last" or "teenth"
"""
Now, you'd probably want to explain "teenth". As it is, I don't entirely follow it. I was trying to grasp it from just the code but it really didn't make sense, even though I did understand what it did. The requirement to include it is strange enough that you ought to clearly document it.
And when invalid input is entered you should actually respond with a clear message about it, so the user quicker realises their mistake. Something like this:
try:
spec_weekday = int(spec_weekday[0:1])
except ValueError:
raise ValueError("spec_weekday must be 'teenth', 'last' or begin with a number,")
Another problem with your input treatment, ' 4th'
would be treated as invalid because of the whitespace. If you just call strip()
on a string, it will remove all whitespace from the start and end. So even if the user bizarrely threw in spaces, tabs and newlines then you can get the valid input out of it. Likewise for testing against strings, you should call .lower()
which will reduce all the characters to lowercase.
spec_weekday = spec_weekday.strip().lower()
With that line at the start, even " \n \t TEenTh "
could be evaluated correctly.
Now, I actually think using a dictionary for your wkday
is a good idea, but you have to do more to implement it correctly. One strength that a dictionary has is the ability to have multiple keys point to the same value. In your case, you can use it for multiple ways to pass the day value. For example:
wkday = {'monday': 0, 'mon': 0,
Now it's even easier to pass valid input, and since Python's a forgiving language it's worth following the paradigm. Especially as you could accept other languages or methods of representing days. Note I also made both keys lowercase, I think you should also strip().lower()
the keys you pass to the dictionary. And again, you could try except KeyError
to raise a proper ValueError
about how that's not a recognised day.
It's bizarre that you have a dictionary for days but months must be integers. Why not have another dictionary for months? Likewise, you could make spec_weekday
's special values a dictionary. And if the key doesn't exist in that dictionary then you can try calling int
instead. Also, int(spec_weekday[0:1])
is the same as int(spec_weekday[0])
(and also int(spec_weekday[:1]
)).
And lastly, weekday
is a confusing term as it means Monday-Friday. Unfortunately the slightly long day_of_week
is the most clear way to name this, or day_name
at least, though that's less clear to read.