10
\$\begingroup\$

I'm taking the 100 Days of Code (Python) taught by Angela Yu on Udemy. There have been a few other questions revolving around this lesson but I can't find one who came up with my specific question so I do apologize if it's out there somewhere.

The course is asking us to check whether or not a year is a leap year using the below flow chart:

If not cleanly divisible by 4 - Not a leap year.
If is cleanly divisible by 4 but not cleanly divisible by 100 - A leap year
If is cleanly divisible by 4, and by 100, but not cleanly divisible by 400 - Not a leap year
If is cleanly divisible by 4, is cleanly divisible by 100 and if is cleanly divisible by 400 - Leap year

The code I came up with does generate the desired outputs and passes the checks required to finish the lesson. I've tested it against a good two dozen years and it's correctly identified whether or not the year was a leap year each time. I'm not getting any syntax errors either. Most of my solutions to the lessons so far have had small variations but this one feels moreso than any of the rest and it's making me question its validity.

The point of the lesson is to get acclimated with if/elif and else statements, so I'm not trying to optimize it or use any more advanced/efficient/shorter keywords. The instructor provides their own solution but does specify there are other ways to go about it.

Is mine just structured poorly? Is there a scenario where it wouldn't work? Again, limiting it to only if/elif and else keywords is very much intentional.

My code:

year = int(input())

if year % 4 != 0:
  print ("Not leap year")
elif year % 100 != 0:
  print ("Leap year")
elif year % 400 !=0:
  print ("Not leap year")
else:
  print ("Leap year")

Instructor:

year = int(input())

if year % 4 == 0:
  if year % 100 == 0:
    if year % 400 == 0:
      print("Leap year")
    else:
      print("Not leap year")
  else:
    print("Leap year")
else:
  print("Not leap year")
\$\endgroup\$
0

2 Answers 2

13
\$\begingroup\$

Your code is nice. Contrasting to the instructor's solution, you have cleanly inverted the arrow anti-pattern to be as 'flat' as possible. I like your code more.

Some possibly uninteresting feedback:

  • Using 2 spaces for indentation is abnormal with Python. Python users typically use 4 spaces for indentation.
    I can understand a JavaScript developer, amoungst some other languages, using 2 spaces for indentation. Or if you're working in a cross language project.

  • Putting a space between the function name print and the opening bracket ( is abnormal. I may be wrong, but I think the convention is abnormal in most programming languages.

  • By changing your code to a function we can easily test if your code is correct. "I've tested it against a good two dozen years" must have been a bit of a pain. We can use calendar.isleap() to test.

  • Personally I try to keep the left side of lines as small as possible and keep the right larger. When reading the following code the small != 0 can easily be skimmed over.

    if really_long_function_name(really_long_variable_name, another_variable) != 0:
    
  • I like the consistancy of your if statements, always using !=.

import calendar


def is_leap_year(year: int) -> bool:
    if 0 != year % 4:
        return False
    elif 0 != year % 100:
        return True
    elif 0 != year % 400:
        return False
    else:
        return True


if __name__ == "__main__":
    for year in range(1, 10_000):
        assert is_leap_year(year) == calendar.isleap(year), f"{year} is mismatched"
    print("All the results are correct!")

One thing to note is we don't actually need to use if statements. If we change to using == then we know %4 is a leep year, %100 cancels out some leap years, and %400 cancels out some cancels out. So we can instead use - to cancel the 100s and + to readd the 400s.

def is_leap_year__ray(year: int) -> bool:
    return bool(
        (0 == year % 4)
        - (0 == year % 100)
        + (0 == year % 400)
    )


if __name__ == "__main__":
    for year in range(1, 10_000):
        assert is_leap_year__ray(year) == calendar.isleap(year), f"{year} is mismatched"
    print("All the results are correct!")

Note: Python's calendars.isleap() source actually uses and and or to perform the canceling which allows for better performance than my code.

\$\endgroup\$
7
  • \$\begingroup\$ Not uninteresting at all! Did toy with Javascript a lot as a kid so the 2 spaces habit makes sense. Is 4 spaces preferable to a tab? I noticed that there's some conflict between the Pep8 and Google's style guide (the latter suggests 2 spaces.) I'd been wondering about the space between print and parenthesis. It felt off but the course hasn't really gone into style guides so I've been winging it. Been trying my best not to skip ahead. But I'm definitely going to include your additions to my notes because it was really beneficial to see the task expanded upon in a more advanced/efficient way. \$\endgroup\$
    – AGM
    Nov 4 at 5:10
  • 2
    \$\begingroup\$ PEP8 explains that four spaces (with no ascii 9 TAB) is normal. But don't give such details a moment's thought. Just run "$ black *.py", plus maybe "$ isort *.py", and be done with it. Your code will look "normal" to other folks you show it to. And then we can focus on what you're saying with the code, rather than trivial details of how you're saying it. (There are details you should sweat. These are not those.) \$\endgroup\$
    – J_H
    Nov 4 at 5:17
  • \$\begingroup\$ @AGM I think the convention is 4 spaces in Python. (Note: Google's Python Style Guide also recomend 4 spaces) "Not uninteresting" I've provided a fair amount of answers now, sometimes people aren't happy with style guide information :) Also "a more [...]/efficient way" isn't true, your code returns early with the if statements so is likely to be more performant than my code, much like how Python's calendars.isleap() is more performat. Mine just condences everything, and can easily be put on one line. \$\endgroup\$
    – Peilonrayz
    Nov 4 at 5:17
  • \$\begingroup\$ @J_H - !! Turns out I was following an old google style guide (my fault for not going to the official source.) Apparently they changed it sometime in the last 2 years. Only came across it because I wound up doing precisely what I said i'd stop doing and skipped ahead to docstrings. Will keep isort in mind in particular since it looks like it'll solve a constant nitpick of mine. And i'll try my best not to sweat the small stuff. Easier said than done for a novice so it's good to hear it from those with more experience. Thank you! \$\endgroup\$
    – AGM
    Nov 4 at 5:29
  • \$\begingroup\$ @Peilonrayz - Feel free to axe this if it's off topic but do you have a preferred alternative to black's? Also I will never find style guide information uninteresting. I really appreciate the tips since they're small things that tend to get skipped in favor of broad-scope basics when it comes to courses like these. \$\endgroup\$
    – AGM
    Nov 4 at 5:30
0
\$\begingroup\$

There is already a good answer but the code isn't the simplest.

A year is a leap year if it is divisible by 4 and not divisible by 100 or it is not divisible by 400. We can use modulus operator to check divisibility, the number is exactly divisible if the remainder is 0. 0 equals False so we can use not to negate it and check when it is False.

We can use and or logic operators to chain the statements.

So the code can be simply written as:

def is_leap_year(year: int) -> bool: 
    return not year % 4 and year % 100 > 0 or not year % 400
\$\endgroup\$
3
  • 1
    \$\begingroup\$ I'll make a note of that! I mentioned this in my post but the goal for this exercise was to use only if, elif and else. No other keywords and no other functions. I wasn't trying to optimize the code so much as I was trying to see if i'd gone wrong by using those keywords in a way that was so different from the instructor's answer. \$\endgroup\$
    – AGM
    Nov 4 at 15:32
  • \$\begingroup\$ You should really use parentheses for a complicated boolean statement. Also, is bool(year % 100 > 0) equivalent to bool(year % 100)? \$\endgroup\$
    – qwr
    Nov 5 at 0:20
  • \$\begingroup\$ @qwr bool(year % 100 > 0) is the same as bool(year % 100), because in Python remainders are always nonnegative, and all values that aren't 0 is interpreted as True, because 0 is False so every nonzero value must be True. \$\endgroup\$ Nov 5 at 4:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.