0

I am currently creating a program which will ask the user to enter a year and then states whether it is a leap year or not by seeing if the year is divisible by 4. It also has a type check, presence check and a length check. I keep on getting the AttributeError: 'int' object has no attribute 'isnumeric'

All in all, the program works well as I want it to (code below) but when the program has finished it states the aforementioned attribute error. Why does it display that message and how can I solve it?
Code:

print("Did you know that the last leap year was in 2020?")
print("To find out if a year is a leap year, use this program")
year = input("Please enter a year\n")
year = str(year)
valid = False

while valid == False:
  if year == "":
    print("Error. Please enter a year - it should be 4 digits long.")
    year = input("Please enter a year\n")
    year = year.decode(year)
    valid == False
  else:
    if year.isnumeric() == False:
      print("Error. Please enter a year - it should be 4 digits long.")
      year = input("Please enter a year\n")
      year = str(year)
      valid == False
    else: 
      if len(year) != 4: 
        print("Error. Please enter a year - it should be 4 digits long.")
        year = input("Please enter a year\n")
        year = str(year)
        valid == False
      else:
        year = int(year)
        rem = year%4
        if rem == 0:
           print(f"{year} is a leap year")
        else: 
           print(f"{year} is not a leap year, unfortunately")
           valid == True
1
  • Not every year that's divisible by 4 is a leap year; 1900 was not a leap year, for example. Commented Apr 18, 2023 at 21:01

1 Answer 1

2

You set year to an int here:

      else:
        year = int(year)

and then the while loop continues with code that assumes year is still a str, including this line:

  else:
    if year.isnumeric() == False:

Note that isnumeric is a method on str objects that tells you whether they contain at least one character and consist of only characters with Unicode properties Numeric_Type=Digit, Numeric_Type=Decimal, or Numeric_Type=Numeric. It is not a test for whether an object is a number, or whether a string represents a number - for example, '-1' and '1.5' report False for isnumeric. int objects are not strings, so they have no such method.

To avoid this problem, I'd suggest separating the part of the code that gets a valid year (as an int) from the part of the code that figures out whether it's a leap year:

print("Did you know that the last leap year was in 2020?")
print("To find out if a year is a leap year, use this program")

# First get the year.
while True:
    try:
        year = int(input("Please enter a year\n"))
        if len(str(year)) != 4:
           raise ValueError("wrong number of digits!")
        break
    except ValueError:
        print("Error. Please enter a year - it should be 4 digits long.")

# year is now an int with 4 digits.  Now figure out if it's leap.
if year % 4 == 0:
    print(f"{year} is a leap year")
    # Note: there is a bug here, leap year calculation
    # is actually more complicated than this!
else: 
    print(f"{year} is not a leap year, unfortunately")

Some general notes on the "get a valid int" loop and how to do this kind of thing more simply than what you tried initially:

  1. No need to convert the result of input() to a str or decode it. input only ever returns a str (in modern versions of Python -- I see you lurking there, pedants).
  2. Instead of trying to predict all the reasons that you might fail to convert a string to an int and writing a bunch of ifs to prevent them, just try the conversion and use an except to catch the ValueError that's raised on a failure. That way you only have to write one error check, and you don't need to do it by trying to reverse-engineer what some other function will do before it does it.
  3. Once you've already written a try/except, you can use a raise in the body of your try to automatically jump to the corresponding except instead of having to write the same error handling code twice.
Sign up to request clarification or add additional context in comments.

3 Comments

This is why, once you have a value in a variable, you don't use the same name for the variable if you convert it to a different type. Use a different name and this problem can never happen.
@kindall what I wouldn't give for a const in Python.
Using mypy is also a good way to avoid this, since it'll infer a type when you first assign a variable and then complain if you assign a different type to it.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.