Search code examples
pythonclasswhile-loopuser-inputpython-3.5

Python 3.5.1: How Do I Make The while-loop Run in class?


I was researching how classes work and I figured out how to make a class work in some scenarios. But I couldn't find a way in which to make my scenario work. It involves class Function():, def __init__(self):, and def AnotherFunction(self, Foo):. Those aren't the actual "Functions" in my scenario but, they are very similar. Here is my code:

class Name():
    def __init__(self, Name):
        self.Name = ["Your Dicer's name is: "]
        self.AddName()
    def AddName(self):
        self.Count = 1
        while(self.Input == 1):
            Input = input("What is your Dicer's name?:\n")
            if(any(BadWord in Input.lower() for BadWord in [])):
                print("That's an inappropriate name! Try again")
            elif(len(Input) < 3):
                print("That name is too short! Try again.")
            elif(len(Input) > 16):
                print("That name is too long! Try again.")
            else:
                self.Count -= 1
        self.Name.append(Input)

My question is, why wont the while loop occur in class? I tried looking up other stack overflow questions, class article and tutorials, and other questions related to while loops but to no avail. Can you tell me why it wont run and how to fix this? I would appreciate that. Thanks! :)

Update 1: I put Input in the AddName parameters. I also added Name as an instance. But these updates didn't work.

Update 2: I took Input out of the AddName parameters. I also changed self.Input to self.Count so it doesn't get confusing. I also changed self.Name += Input to self.Name.append(Input). These updates made the code somewhat easier to understand and fixed some of the problems but, the while loop wont run.


Solution

  • The immediate issue is on these lines:

        self.AddName()
    def AddName(self, Input):
    

    The second one shows that the AddName expects one argument in addition to self. The previous line however, is a call that doesn't pass any argument. You need to make those two match up properly.

    I think in this case, you don't need an Input parameter for AddName. The Input variable you're using in the function comes from user input (the input function), and the argument is completely ignored. (Note, there's also a self.Input attribute, which is unrelated other than having a very confusingly similar name).

    Here's a minimally fixed version:

    class Name():
        def __init__(self):
            self.Name = ["Your Dicer's name is: "]
            self.AddName()
        def AddName(self):              # no Input paramter needed on this line!!!!
            self.Input = 1
            while(self.Input == 1):
                Input = input("What is your Dicer's name?:\n")
                if(any(BadWord in Input.lower() for BadWord in [])):
                    print("That's an inappropriate name! Try again")
                elif(len(Input) < 3):
                    print("That name is too short! Try again.")
                elif(len(Input) > 16):
                    print("That name is too long! Try again.")
                else:
                    self.Input -= 1
            self.Name += Input           # you may want self.Name.append(Input) here
    

    This "works" in the sense that it doesn't raise an exceptions, but it probably doesn't quite do what you want. The last line, self.Name += Input appends each character of the inputted name onto the list self.Name. That's not very useful. Probably you want self.Name.append(Input) or maybe self.Name shouldn't be initialized to be a list in __init__.

    There are also a vast number of style issues with this code. I'd strongly recommend renaming all of the variables and attributes. Normal Python style is to use lowercase_names_with_underscores for functions, methods, attributes and most variables. Only classes have CapitalizedNames. The self.Input attribute you're using is also confusingly similar to the local variable Input while they don't really mean anything very close to one another (there's also no need for self.Input to be an instance attribute, rather than a local variable of some kind).

    Here's a rewrite of your code that doesn't do much different, but has much better style:

    class Name():
        def __init__(self):
            while True:
                name = input("What is your Dicer's name?:\n")
                if(any(bad_word in name.lower() for bad_word in [])):
                    print("That's an inappropriate name! Try again")
                elif(len(name) < 3):
                    print("That name is too short! Try again.")
                elif(len(name) > 16):
                    print("That name is too long! Try again.")
                else:
                    self.name = name
                    break
    
        def __str__(self):
            return "Your Dicer's name is: {}".format(self.name)
    

    If you're going to call the AddName method just once from __init__, there may not be much point in having it be a separate function. Here I've just merged it right in to __init__. I've also done away with self.Input, in favor of a while True loop, and use break to exit it when we get a valid name. The potentially invalid name is being stored in a variable named name rather than Input, and I've moved the "Your Dicer's name is" text to the __str__ method, rather than having it be part of the name attribute.

    But I'm not really sure why you're using a class here in the first place. Usually a class represents a concrete "thing" that exists in your program. A Name is a very abstract sort of "thing". While it's not always unreasonable to have a Name class, more often, a name would simply be an attribute attached to some other object (like a Dicer, perhaps, whatever that is).