Buidling classes with user input and calling methods Learned alot in this first week, following PCC, but biggest leaps came from the community and others giving their time to help, thank you!

All input appreciated!

ObjectiveGoals were to fill a class with user inputs from multiple users and then be able to call methods to each user input created class. (... did I say that right O.o)

Code

class User():
    def __init__(a, first_name, last_name, city, age):
        a.first_name = first_name.title()
        a.last_name = last_name.title()
        a.city = city.title()
        a.age = age

    def describe_user(a):
        print("-----")
        print("First Name" + " : " + a.first_name)
        print("Last Name" + " : " + a.last_name)
        print("City" + " : " + a.city)
        print("Age" + " : " + a.age)

def ask_user(message=''):
    user_input = ''
    while not user_input:
        user_input = input(message)
    return user_input

def form_complete(values, placement, length):
    placement = []
    while len(placement) &lt; length:
        first_name = ask_user("Enter First Name: ")
        last_name =  ask_user("Enter Last Name: ")
        city = ask_user("Enter City: ")
        age = ask_user("Enter Age: ")
        values = User(first_name, last_name, city, age)
        placement.append(values)
    return placement

if __name__ == '__main__':

    users = form_complete('user', 'users', 3)
    for a in range(len(users)):
        users[a].describe_user()


Output

xenial)vash@localhost:~/pcc/9$ python3 3.py
Enter First Name: vash
Enter Last Name: the stampede
Enter City: gunsmoke
Enter Age: 131
Enter First Name: spike
Enter Last Name: spiegel
Enter City: mars
Enter Age: 27
Enter First Name: holden
Enter Last Name: caulfield
Enter City: new york city
Enter Age: 16
-----
First Name : Vash
Last Name : The Stampede
City : Gunsmoke
Age : 131

Greetings Vash!
-----
First Name : Spike
Last Name : Spiegel
City : Mars
Age : 27

Greetings Spike!
-----
First Name : Holden
Last Name : Caulfield
City : New York City
Age : 16

Greetings Holden!
(xenial)vash@localhost:~/pcc/9$
Python has something called magic methods (sometimes also called dunder methods, but the other name is way cooler).

These methods have special names and enable custom classes to use built-in functionality. If, for example you write a custom numeric class, you would want to be able to do e.g. a + b, with at least one of them being an instance of your class (and the other one either as well or even just a plain number). In order to achieve this, you would just implement an appropriate __add__ method for your class.

Here is a good list going through all available magic methods.

In your class, we can use the __str__ method. It gets used when you call str(obj), print(obj), "{}".format(obj) (unless you also implement the __format__ method):

class User:
    def __init__(self, first_name, last_name, city, age):
        self.first_name = first_name
        self.last_name = last_name
        self.city = city
        self.age = age

    def __str__(self):
        s = ["-----",
             f"First Name : {self.first_name}",
             f"Last Name : {self.last_name}",
             f"City : {self.city}",
             f"Age : {self.age}"]
        return "\n".join(s)


Note that I made some more changes:


The empty parenthesis after the class name are not needed. You only need parenthesis if you want to inherit from some other class.
I also called the first argument of the methods self, which is the customary name for it in Python.
I did not build the string using simple addition, since that is quite slow in Python (strings are immutable objects, so each string addition involves creating a new string of the right length and copying the content of the two strings being added).
And finally, I used f-strings to make the formatting a bit easier.




In your form_complete function (which IMO should be called complete_form, since that describes an action), you could use a list comprehension:

def complete_form(n_users):
    return [User(ask_user("Enter First Name: ").title(),
                 ask_user("Enter Last Name: ").title(),
                 ask_user("Enter City: ").title(),
                 ask_user("Enter Age: ", type=int)
            for _ in range(n_users)]


Note that I moved the alls to str.title here, since it should be the responsibility of the user of your class to pass in correct values (for example, what if your user enters a name for which title() does not make sense? There are also names that start with lowercase letters. Etc...)

I also removed values and placement from the signature, since you override them right away anyways.



And finally, I added a keyword argument to ask_user, which allows you to validate that age is actually an integer:

def ask_user(message='', type=str):
    user_input = ''
    while not user_input:
        try:
            user_input = type(input(message).strip())
        except ValueError:
            continue
    return user_input


By default, this just calls the str function on whatever your user supplied. If you instead specify type=int, it will call int on the input, which raises a ValueError if it cannot parse the input it received as an int.

This has only one weakness right now: If the user wants to enter something that parses as 0, they are stuck in an infinite loop (so no users under the age of 1 allowed).

I also added a call to str.strip, so user names like  (space) and  (tab) are not allowed either.