-1

Feel embarrassed asking this Q. I have been trying to figure out how to get ride of "None" output that appears from my def Function. My function is to calculate the factorial of a number, but to handle errors, i did a try and except but when i do that I get "None" printed along with it. Thanks in advance for your help.

def factorial(number):
    """ This function will calculate the factorial (N!) of a inputted number """
    
    output = "null"
    try:
        if int(number) > 0:
            return number * factorial(number - 1)
           
    
        elif number == 0:
            return 1
        
        else:
            return output
    except:
        print("Error, please ensure you are inputting a integer.")

print(factorial("xyz"))

#^ Prints out the proper except block but "None" follows this output. 
  • 2
    Just add a `return 'null'` (or whatever you want) to your `except` block – Cory Kramer Sep 15 '21 at 17:03
  • 1
    Does this answer your question? [Why is this printing 'None' in the output?](https://stackoverflow.com/questions/28812851/why-is-this-printing-none-in-the-output) – wjandrea Sep 15 '21 at 17:04
  • 2
    OK, so this is not how you would write a general purpose function like factorial. The function should return a value when the input is valid and it should raise an exception or return None when the input is invalid. Which you implement is your decision on what you want your interface to be. The factorial function should not print anything, ever. It is the caller's responsibility to detect failure and deal with it as they choose (e.g. print a message). – jarmod Sep 15 '21 at 17:05
  • 2
    @CoryKramer returning 'null' is no better than what the code is already doing, which is to implicitly return None. In both cases the caller has to perform some test on the returned value. – jarmod Sep 15 '21 at 17:07
  • 2
    @jarmod *"The function should return a value when the input is valid"* -- Yes, definitely! *"or return None when the input is invalid"* -- No, don't do that. Using a sentinel value to indicate an error is a huge pain when exception handling is so easy. – wjandrea Sep 15 '21 at 17:09
  • 1
    Beside the point, but [a bare `except` is bad practice](/q/54948548/4518341). Instead, use the specific exception you're expecting like `except ValueError`, or at least `except Exception`. – wjandrea Sep 15 '21 at 17:10
  • @wjandrea I would recommend the exception path, for sure. Raise a ValueError, for example. – jarmod Sep 15 '21 at 17:10

2 Answers2

3

When the except block handles an exception, the factorial function does not explicitly return a value, so the function returns None. Then, the None is passed to the print function. It is as though the code is written like this:

result = factorial("xyz")  # result is None
print(result)              # this is where "None" is printed

I think the best way to solve this is to separate parsing the number from the factorial function:

def factorial(number):
    """ This function will calculate the factorial (N!) of a number """
    
    output = "null"
    if number > 0:
        return number * factorial(number - 1)
    elif number == 0:
        return 1
    else:
        return output

try:
    # first, parse the input
    number = int("xyz")

    # Now that `number` is guaranteed to be an integer, pass it
    # to the factorial function.
    print(factorial(number))
except:
    # If the input was not an integer, this is where the
    # exception is handled. The print function was not called
    # in the try block, so there is no extra "None" in the output
    print("Error, please ensure you are inputting a integer.")

This works, but the factorial function now sometimes returns a number and sometimes returns the string "null". It would be better if it either returned a number, or it threw an exception. We can raise a ValueError if the user provides a negative number:

def factorial(number):
    """ This function will calculate the factorial (N!) of a number """
    
    if number > 0:
        return number * factorial(number - 1)
    elif number == 0:
        return 1
    else:
        raise ValueError("Number must not be negative")

def parse_and_compute_factorial(input):
    try:
        # first, parse the input
        number = int(input)
    except ValueError:
        print("Error, please ensure you are inputting a integer.")
        return

    try:
        print(factorial(number))
    except ValueError as err:
        print(err)

parse_and_compute_factorial("xyz") # prints "Error, please ensure you are inputting a integer"
parse_and_compute_factorial("-5")  # prints "Number must not be negative"
parse_and_compute_factorial("5")   # prints "120"
Stephen Jennings
  • 12,494
  • 5
  • 47
  • 66
1

The error is caused by the lack of a return under except.

When you try to print the result from factorial('xyz'), it fails at the try, goes to except, prints a string, and then because you didn't define a return value, it returns None. So, you print None.

You could fix this by just re-writing your except catch to return that string instead of printing it.

JohnAlexINL
  • 615
  • 7
  • 15