Introduction
A leap year (in the Gregorian calendar) occurs:
- In every year that is evenly divisible by 4.
- Unless the year is evenly divisible by 100, in which case it’s only a leap year if the year is also evenly divisible by 400.
Some examples:
- 1997 was not a leap year as it’s not divisible by 4.
- 1900 was not a leap year as it’s not divisible by 400.
- 2000 was a leap year!
For a delightful, four-minute explanation of the whole phenomenon of leap years, check out [this YouTube video](https://www.youtube.com/watch?v=xX96xng7sAE).
Instructions
Instructions
Your task is to determine whether a given year is a leap year.
Dig Deeper
Boolean chain
Chain of boolean expressions
def leap_year(year):
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
This might be considered the “most idiomatic” or “most Pythonic” solution, as it is exactly the same as the code implemented by the maintainers of the Python language for the calendar.isleap() method.
The first boolean expression uses the modulo operator to check if the year is evenly divided by 4.
- If the year is not evenly divisible by
4, then the chain will short circuit due to the next operator being a logical AND {and), and will return False.
- If the year is evenly divisible by
4, then the year is checked to not be evenly divisible by 100.
- If the year is not evenly divisible by
100, then the expression is True and the interpreter will stop the evaluation to return True, since the next operator is a logical OR (or).
- If the year is evenly divisible by
100, then the expression is False, and the returned value from the chain will be if the year is evenly divisible by 400.
| year | year % 4 == 0 | year % 100 != 0 | year % 400 == 0 | is leap year |
|---|
| 2020 | True | True | not evaluated | True |
| 2019 | False | not evaluated | not evaluated | False |
| 2000 | True | False | True | True |
| 1900 | True | False | False | False |
The chain of boolean expressions is efficient, as it proceeds from testing the most to least likely conditions.
It is the fastest approach when testing a year that is not evenly divisible by 100 and is not a leap year.
Operator precedence
The implementation contains one set of parentheses, around the or clause:
- One set is enough, because the
% operator is highest priority, then the == and != relational operators.
- Those parentheses are required, because
and is higher priority than or.
In Python, a and b or c is interpreted as (a and b) or c, which would give the wrong answer for this exercise.
If in doubt, it is always permissible to add extra parentheses for clarity.
Refactoring
By using the falsiness of 0, the not operator can be used instead of comparing equality to 0.
For example:
def leap_year(year):
return not year % 4 and (year % 100 != 0 or not year % 400)
It can be thought of as the expression not having a remainder.
Ternary operator
Ternary operator
def leap_year(year):
return not year % 400 if not year % 100 else not year % 4
A ternary operator uses a maximum of two checks to determine if a year is a leap year.
It starts by testing the outlier condition of the year being evenly divisible by 100.
It does this by using the modulo operator.
Also, by using the falsiness of 0, the not operator can be used instead of comparing equality to 0.
- If the year is evenly divisible by
100, then the expression is True, and the ternary operator returns if the year is evenly divisible by 400.
- If the year is not evenly divisible by
100, then the expression is False, and the ternary operator returns if the year is evenly divisible by 4.
| year | year % 100 == 0 | year % 400 == 0 | year % 4 == 0 | is leap year |
|---|
| 2020 | False | not evaluated | True | True |
| 2019 | False | not evaluated | False | False |
| 2000 | True | True | not evaluated | True |
| 1900 | True | False | not evaluated | False |
Although it uses a maximum of only two checks, the ternary operator tests an outlier condition first,
making it less efficient than another approach that would first test if the year is evenly divisible by 4,
which is more likely than the year being evenly divisible by 100.
The ternary operator was fastest in benchmarking when the year was a leap year or was evenly divisible by 100,
but those are the least likely conditions.
datetime addition
datetime addition
import datetime
def leap_year(year):
return (datetime.datetime(year, 2, 28)
+ datetime.timedelta(days=1)).day == 29
This approach may be considered a "cheat" for this exercise, which is intended to practice Boolean operators and logic.
It also adds a tremendous amount of overhead in both performance and memory, as it imports all of the `datetime` module and requires the instantiation of both a `datetime` object and a `datetime.timedelta` object.
For more information, see this exercises performance article.
By adding a day to February 28th for a given year, you can see if the new day falls on the 29th of February, or the 1st of March.
If it is February 29th, then the function returns True for the year being a leap year.
- A new datetime object is created for February 28th of the year.
- Then the timedelta of one day is added to that
datetime,
and the function returns if the day property of the resulting datetime object is the 29th.
Source: Exercism python/leap