0
\$\begingroup\$

I want to sanitize my user input in my class. All is working as expected but first of all, I would like to avoid to set a default value for my digit parameter. Secondly, I would like to avoid to put everything in a big while-loop and instead call implicitly a function which asserts the user input.

Initially, I thought to create a @staticmethod could do the job but I was obviously wrong (Why does staticmethod not assert my user input?).

My code is:

class CompositeNumbers:
    def __init__(self, digit=6):
        self.uinput = digit

        flag = True

        while flag:

            try:
                self.uinput = int(input("Enter a number: "))

                while self.uinput <= 0:
                    self.uinput = int(input("Please enter a positive number: "))

                else:
                    flag = False
                    for i in range(2, self.uinput):
                        if self.uinput % i == 0:
                            print("The number is composite!")
                            break
                        else:
                            print("The number is a prime!")
                            break

            except ValueError:
                print("Wrong input!")

EDIT

My previous attempt to use a function did not work as I wanted it to. I would need to call the check_input function. It would check my input but if I would call the userinput function, it would not check the input implicitly.

@staticmethod
def check_input(digit):
    try:
        while digit <= 0:
            digit = int(input("Try again: "))
    except TypeError:
        print("Wrong Input")


def userinput(self):

        uinput = self.digit

        for i in range(2, uinput):
            if uinput % i == 0:
                print("The number is composite!")
                break
            else:
                print("The number is a prime!")
                break
\$\endgroup\$
2
  • \$\begingroup\$ (1) Why do you not just omit the default value if you don't want it? (2) I can't imagine how such an implicit assertion should work because if it fails you would need a loop anyway to ask for a valid value. Better: Create an additional function, e.g. askForValidNumber which contains an endless loop to take the input, check it and return it only if it is valid. \$\endgroup\$ Commented Dec 5, 2018 at 21:25
  • \$\begingroup\$ (1) Without the default value I would need to enter a digit in my initial calling variable: p1 = CompositeNumbers(6); p1 (2) I created in my SO account the question because my @staticmethod was unsatisfiable (see my edit). \$\endgroup\$
    – Alex_P
    Commented Dec 6, 2018 at 10:29

1 Answer 1

2
\$\begingroup\$

Welcome! Let's look at your code:

  • I notice that you have code that appears to be part of a class, so I will be treating it as a class for the purposes of review. In your original code, you use a class to group all your operations together, but the class doesn't really serve a purpose (i.e. the method could exist independent of the class, with a name like is_prime). In your second version, you have two separate method, and while the encapsulation of different functionally is better, they are not connected in a meaningful way so it is not clear how you run the program. In both cases, a class is unnecessary: you could have a single function that prompts the user for a positive integer input, and another function that checks if a positive integer is prime or composite (which I will demonstrate below). Personally I store my input validation methods in a separate module that I've added to my PYTHONPATH, since it's useful for many different projects if you use the command line often.
  • Commenting in general on your goal to "check the input implicitly", I would say this is misguided, but I will also say that you can use a property decorator to implicitly check variables on assignment. However, as Michael mentions you still would need to loop to prompt input from the user until they enter valid input, so it's not really that usefully for input validation.

Now, if you're trying to validate input in Python, this SO answer provides a good primer on the subject.

  • Your check_digit method checks if the input is valid before the user actually enters any input. As mentioned in the above answer, while True and with an if valid: break is a better structure for this kind of validation.
  • Your userinput method is ambiguously named (you can't tell what it does from the name alone), and it's broken: it only checks if 2 is prime before breaking. You need to only check if the number is composite inside the loop, and return prime if a composite factor is not detected.

Here's code that demonstrates the principles I mention above:

def prompt_positive_number(message):
    while True:
        try:
            num = int(input(message))
            if num <= 0:
                message = "Please enter a positive number. "
            else:
                return num
        except ValueError:
            message = "Please enter a number. "

def is_prime(num):
    for i in range(2, num):
        if num % i == 0:
            return False
    return True

if __name__ == "__main__":
    user_input = prompt_positive_number("Check if a number is prime: ")
    if is_prime(user_input):
        print(user_input, "is prime!")
    else:
        print(user_input, "is composite!")

There's one more optimization I can use to make my code even shorter: using the built-in function any for is_prime:

def is_prime(num):
    return not any(num % i == 0 for i in range(2, num))

Or, reversing the equality check, all is even shorter:

def is_prime(num):
    return all(num % i != 0 for i in range(2, num))

Finally, commenting on the algorithm, there are more efficient ways to check if a number is prime; however, this is outside of the scope of my knowledge and you'd have to do your own research on that. Rather famously in the world of computational complexity, it was proved that primes is in P.

\$\endgroup\$
2
  • \$\begingroup\$ What an excellent answer. Thank you very much for your effort! May I ask why it is misguided to try to validate the input implicitly? \$\endgroup\$
    – Alex_P
    Commented Dec 8, 2018 at 15:43
  • \$\begingroup\$ @Alex_P Unless you have some kind of abstraction where the input validation really will be generalized across multiple structures, there's no reason to have it done implicitly. Like the Zen of Python says, "Explicit is better than implicit." For instance, if you decided to change what numbers are acceptable, by for example only accepting numbers greater than one, since 1 is trivially neither prime nor composite, than having it explicit makes it easier to determine how to make this fix. It's less code if there's no larger abstraction. \$\endgroup\$
    – Graham
    Commented Dec 8, 2018 at 16:22

Your Answer

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

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