The main problem with your code is that you are incorrectly using a class method, which doesn't have access to the instance variables that needs to be validated and replaced. Review the related post on the Difference between class and instance methods to understand what a class method is and what it's for.
Furthermore, your code
return cls(Name,Age,Sex)
instantiates a new, different object, different from the original one that was just instantiated with the invalid Age
, and, as tadman pointed out in the comments, can lead to a continuous chain of instantiation, validation, instantiation, ... etc. which doesn't even modify the original instance. This is definitely not what you want.
The simple, yet a bit naive solution, would be to replace your class method with an instance method and then use a regular while
loop to keep prompting for input until you get a valid Age
, similar to what's demonstrated in JustLearning's answer:
In [4]: class Player:
...: def __init__(self, Name, Age, Sex):
...: self.Name = Name
...: self.Age = Age
...: self.Sex = Sex
...:
...: self.validate()
...:
...: def validate(self):
...: while (not isinstance(self.Age, int)) or self.Age < 0:
...: self.Age = int(input('Please input a valid integer greter than 0'))
But, while that certainly works, it's highly unusual and a bit of code smell to have an input prompt as part of instantiating a class. The better practice would be prompting for Age
and getting all the other variables before instantiating the class, as part of a separate function. Your class should just act like a container for a Player
's details and functions, and this would also make it much easier to write unit tests for the Player
class.
Furthermore, doing int(input(...)
can possibly raise a ValueError
Exception if the input isn't a valid integer. Checking with isinstance
would not properly handle non-integer inputs. See Asking the user for input until they give a valid response for better ways to validate.
My alternative solution would be:
In [22]: class Player:
...: def __init__(self, Name, Age, Sex):
...: self.Name = Name
...: self.Age = Age
...: self.Sex = Sex
...:
...: def showDetails(self):
...: return f'Player name: {self.Name}. Player age: {self.Age}'
...:
In [23]: def get_age():
...: while True:
...: try:
...: Age = int(input('Please input a valid integer greater than 0 '))
...: except ValueError:
...: continue
...: if Age < 0:
...: continue
...: else:
...: break
...: return Age
...:
In [24]: Age = get_age()
Please input a valid integer greater than 0 -5
Please input a valid integer greater than 0 -16
Please input a valid integer greater than 0 notinteger
Please input a valid integer greater than 0 22
In [25]: a = Player('Nail',Age,'F')
In [26]: a.showDetails()
Out[26]: 'Player name: Nail. Player age: 22'
If somehow you really still want your class to do some validation, it should just raise some form of an Exception if it gets invalid input, and then let your outer code handle it.
In [32]: class Player:
...: def __init__(self, Name, Age, Sex):
...: self.Name = Name
...: self.Age = Age
...: self.Sex = Sex
...: self.validate()
...:
...: def validate(self):
...: if (not isinstance(self.Age, int)) or self.Age < 0:
...: raise ValueError("Age should be int and >0")
In [36]: try:
...: Age = get_age()
...: a = Player('Nail', Age, 'F')
...: except ValueError:
...: print("Cannot instantiate Player, invalid details")
Finally, since this answer is all about best practices, you should follow PEP8 Style Guide for Naming Conventions and use all-lowercase for function parameters and instance variable names:
class Player:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
Only use uppercase/CapWords style Age
only if Age
is a class.