← All coding practice

Coding Practice · Beginner · October 15, 2026 · 7 min read

Python Coding Exercise #40: Leap Year

Part of the series Python Practice Exercise — part 40

Before you start: Know input() and int() · Know the % operator · Know if, elif and else

The question

Generally, any year that is divisible by four is a leap year. However, if the year is additionally divisible by 100, it is a leap year only if it also divisible by 400.

Please write a program which asks the user for a year, and then prints out whether that year is a leap year or not.

Some examples:

Please type in a year: 2011
That year is not a leap year.
Please type in a year: 2020
That year is a leap year.
Please type in a year: 1800
That year is not a leap year.

My Solution

year = int(input("Please type in a year: "))
if year % 400 == 0:
    print("That year is a leap year.")
elif year % 100 == 0:
    print("That year is not a leap year.")
elif year % 4 == 0:
    print("That year is a leap year.")
else:
    print("That year is not a leap year.")

How My Solution Works

1. Understanding the rule

The rule has three layers, and each one overrides the one before it:

  1. A year divisible by 4 is a leap year. (2020, 2024)
  2. But if it is also divisible by 100, it is not a leap year. (1900, 2100)
  3. Unless it is also divisible by 400, in which case it is a leap year again. (1600, 2000)

So 2020 is a leap year, 1900 is not, and 2000 is. The % operator checks divisibility, because year % 4 == 0 means “dividing by 4 leaves a remainder of 0”. (See Exercise #39 for more on divisibility.)

2. Check the most specific condition first

Every multiple of 400 is also a multiple of 100, and every multiple of 100 is also a multiple of 4. The conditions are nested inside each other like this:

divisible by 400  ⊂  divisible by 100  ⊂  divisible by 4

Python runs only the first branch that’s true, so My Solution checks the narrowest group first, then the next one out, and so on:

YearFirst true branchPrinted
2000year % 400 == 0That year is a leap year.
1900year % 100 == 0That year is not a leap year.
2020year % 4 == 0That year is a leap year.
2011none, so the else runsThat year is not a leap year.

In words:

  • If the year is divisible by 400, it’s a leap year.
  • Otherwise, if it’s divisible by 100, it’s not a leap year (the 400 case has already been handled, so it must be a century year like 1900).
  • Otherwise, if it’s divisible by 4, it’s a leap year.
  • Otherwise, it’s not.

This is the same idea as in Exercise #39 and Exercise #36: put the narrowest condition first.

The trap: checking 4 first

It’s tempting to follow the rule in the order it’s written: “divisible by 4 means leap year”, and deal with the exceptions afterwards.

year = int(input("Please type in a year: "))
if year % 4 == 0:
    print("That year is a leap year.")
elif year % 100 == 0 and year % 400 != 0:
    print("That year is not a leap year.")
else:
    print("That year is not a leap year.")

This works for 2011 and 2020, but it’s wrong for 1800, 1900 and 2100. Those years are divisible by 4, so the first condition is already true, and the century exception is never reached. The program prints That year is a leap year. for 1900, which is wrong. The second branch can never run for any year, because every year divisible by 100 is also divisible by 4. Checking 4 first hides the exceptions, so the general rule has to come last.

The samples include 1800, which is exactly the kind of year that catches this mistake.

University Solution

year = int(input("Please type in a year: "))

# First, we make assumption that a year is not a leap year
leap_year = False

if year % 100 == 0:
    if year % 400 == 0:
        leap_year = True
elif year % 4 == 0:
    leap_year = True

if leap_year:
    print("That year is a leap year.")
else:
    print("That year is not a leap year.")

I found the University Solution more advanced than mine, because it uses three ideas that the earlier exercises didn’t:

1. A nested if. The condition year % 400 == 0 sits inside the if year % 100 == 0 block, indented one level deeper. It’s only checked when the year is divisible by 100. This mirrors the wording of the rule: “if the year is divisible by 100, it’s a leap year only if it’s also divisible by 400”.

2. A flag variable. The program starts with leap_year = False, which is the assumption that the year is not a leap year, and then changes it to True only in the cases where it is. The decision about what to print is made once, at the end, with if leap_year:. (A variable that holds True or False like this is called a flag. It’s the same idea as is_of_age in Exercise #32.)

3. An elif that belongs to the outer if. This is the subtle part. The line elif year % 4 == 0: is lined up with if year % 100 == 0:, not with the inner if. So it belongs to the outer if / elif pair, and it only runs when the year is not divisible by 100. It’s easy to read it as part of the nested block, because it comes right after it.

Here is how four years go through the University Solution:

Yearyear % 100 == 0?Inner year % 400 == 0?elif year % 4 == 0?leap_year
1900yesnoskippedFalse
2000yesyesskippedTrue
2020nonot checkedyesTrue
2011nonot checkednoFalse

The final print() then shows the message that matches the flag. Both solutions give the same, correct answer for every year. Mine is a flat chain that reads from top to bottom, which I found easier to follow. The University Solution follows the wording of the rule more closely, but it needs more care to trace.

Other ways to write it

One condition. The rule can be written as a single combined condition using and and or:

year = int(input("Please type in a year: "))
if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
    print("That year is a leap year.")
else:
    print("That year is not a leap year.")

It reads as: “divisible by 4, and either not divisible by 100, or divisible by 400”. The brackets make the grouping clear. This is the shortest version, but it’s the hardest to read at first glance, and the rule is packed into one line. (and and or were covered in Exercise #39 and Exercise #37.)

A built-in function. Python’s calendar module already knows the rule:

from calendar import isleap

year = int(input("Please type in a year: "))
if isleap(year):
    print("That year is a leap year.")
else:
    print("That year is not a leap year.")

isleap(year) returns True or False. It gives the same results as the other versions. The exercise is about writing the rule yourself, but in everyday code the built-in function is the right tool.

Why the 400 rule exists

A year is not exactly 365.25 days long. It’s a little shorter, about 365.2425 days. Adding a leap day every 4 years is slightly too much, so the calendar skips it in century years, except those divisible by 400. Over any 400-year cycle, that gives 97 leap years instead of 100, which keeps the calendar closely in step with the seasons.

Key takeaways

  • year % 4 == 0 checks divisibility by 4. The % operator gives the remainder.
  • The leap year rule has layers: divisible by 4 is a leap year, except century years, unless they are divisible by 400.
  • In an if / elif chain, put the narrowest condition first. Here that’s 400, then 100, then 4.
  • Checking 4 first hides the century exceptions, because every year divisible by 100 is also divisible by 4.
  • A nested if is an if inside another if. An elif belongs to the if it is lined up with.
  • A flag variable such as leap_year stores a True or False decision, so it can be used once at the end.
  • Test the century years, such as 1900, 2000 and 2100, not just the everyday ones.
  • The messages must match the sample wording exactly.

✦ This is part of my Python Practice Exercise series, where I work through exercises while learning Python and explain each answer in plain language. ✦


Practice Source: This exercise is taken from the Python Programming MOOC 2026 by the University of Helsinki. I’m working through the exercises as part of my Python learning journey and documenting my answers and explanations here.

Tagged: python · conditionals · modulo · nested-if · logical-operators