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
1 Answer 1
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 anif 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 if2
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.
-
\$\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– Alex_P2018年12月08日 15:43:15 +00:00Commented 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– Graham2018年12月08日 16:22:31 +00:00Commented Dec 8, 2018 at 16:22
Explore related questions
See similar questions with these tags.
askForValidNumber
which contains an endless loop to take the input, check it and return it only if it is valid. \$\endgroup\$p1 = CompositeNumbers(6); p1
(2) I created in my SO account the question because my @staticmethod was unsatisfiable (see my edit). \$\endgroup\$