trying to simplify some boolean statements in python - python

I'm "newish" to python programming. I'm trying my best to make my code look nice and function well. I'm using Pycharm as my IDE. I'm doing something for myself. I play tabletop RPG's and I'm attempting to create a character creator for a game I play. I have everything working well, but Pycharm is telling me that "Expression can be simplified" and "PEP 8: E712 comparison to True should be 'if cond is not True:' or 'if not cond:'"
Here is the code in question:
fname = False
while fname != True:
new_character.firstName = input('What would you like your first name to be?\n').capitalize()
if 1 >= len(new_character.firstName) or len(new_character.firstName) > 20:
print('Name does not meet length requirements. Please try again.')
if new_character.firstName.isalpha() != True:
print('Please do not use numbers or special characters in your name. Please try again.')
if (1 < len(new_character.firstName) < 20) and (new_character.firstName.isalpha() == True):
fname = True
Pycharm is telling me that my "while fname != True:" is the part that can be simplified as well as the "if new_character.firstName.isalpha() != True:".
I've tried googling a solution for what I'm doing, but most of them are for something kinda like what I'm asking, but never with the != True portion. I've even reached out to one of my friends that's a python programmer, but I haven't heard back yet.
Again, I want to state that as it is now, the code works correctly the way it is written, I'm just wanting to understand if there is a way to make the code look cleaner/neater or do the same function and be simplified somehow.
Any pointers on how to potentially simplify those lines of code and maintain the functionality would be greatly appreciated.

Here's one way you could rewrite this code to make it easier to read, and more efficient:
# Loop until the user provides a good input
while True:
# Set a temp variable, don't constantly reassign to the new_character.firstName attribute
name = input('What would you like your first name to be?\n').capitalize()
# If the name isn't between 2 and 20 characters, start the loop over at the beginning
if not (1 < len(name) <= 20):
print('Name does not meet length requirements. Please try again.')
continue
# If the name contains anything other than letters, start the loop over at the beginning
if not name.isalpha():
print('Please do not use numbers or special characters in your name. Please try again.')
continue
# You can only reach this break if the name "passed" the two checks above
break
# Finally, assign the character name
new_character.firstName = name
One thing you could do to simplify further is to check both conditions at the same time, and print a more helpful error message that re-states the requirements explicitly:
NAME_ERROR_MESSAGE = """
Invalid name '{name}'. Your character's name
must be between 2 and 20 characters long, and
contain only letters. Please try again.
"""
while True:
name = input('What would you like your first name to be?\n').capitalize()
if (1 < len(name) <= 20) and name.isalpha():
new_character.firstName = name
break
print(NAME_ERROR_MESSAGE.format(name=name)

Related

Python 3.6.2 loop not working like I wanted it too

I am currently writing a code for my GCSE coursework and I am kind of stuck with my for loop which also contains an if-else statement.
I have done a code similar to this earlier in the program and it works perfectly fine but for some reason this part doesn't and I was wondering if someone could help me.
What I am trying to do is make a quiz type program and the part that I need help with is choosing the subject that the user wants to do.
The user has to type in their preferred subject but if they type the subject in wrong, or type in something invalid, then the program should allow the user to type it in again.
So far, if you type in a subject correctly the first time, the program will proceed to the next stage.
However, if you type it incorrectly the first time, it will ask the user to try again. But if you type it in correctly the second time, it will again ask the user to try again. Instead of having the program make the user type the subject again, even though it should've been valid the when they typed it in correctly, I want the program to proceed to the next stage.
Available subjects:
subjects = []
algebra = ("algebra")
computing = ("computing")
subjects.append(algebra)
subjects.append(computing)
Part that I need help with:
with open("student_file.csv", "a+") as studentfile:
studentfileReader = csv.reader(studentfile, delimiter = ',')
studentfileWriter = csv.writer(studentfile, delimiter = ',')
print("Available subjects:\n-Algebra\n-Computing\n")
ChosenSubject = input("What subject would you like to do? ")
ChosenSubject.lower()
for i in range(2):
if ChosenSubject in subjects:
print("\n")
break
else:
print("\nPlease try again.")
ChosenSubject == input("What subject would you like to do?")
ChosenSubject.lower()
if ChosenSubject in subjects:
print("working")
else:
print("You keep typing in something incorrect.\nPlease restart the program.")
In the else block, perhaps you'd want to replace the '==' with '='.
Also do you want to give the user just two tries or keep asking them until they answer correctly? (The latter is what I inferred from your question, for that I'd recommend using continue)
The for loop just iterates over a collection of objects. Consider a list my_list = ['a', 'b', 'c']. On each iteration over my_list using for loop, it fetches one of the elements in order without repetition. range(2) is equivalent to [0, 1].
Try this:
print("Available subjects:\n-Algebra\n-Computing\n")
for i in range(2):
# `i` is 0 on first iteration and 1 on second. We are not using `i` anywhere since all we want is to loop :)
chosen_subject = input("What subject would you like to do? ")
if chosen_subject.lower() in subjects:
print("\n")
break
if chosen_subject.lower() in subjects:
print("working")
else:
print("You keep typing in something incorrect.\nPlease restart the program.")
This is not an optimal solution, but since your learning I will try to keep it as close as your solution. Your problem is that calling ChosenSubject.lower() does not change the actual value in ChosenSubject.
Here is a working example:
print("Available subjects:\n-Algebra\n-Computing\n")
ChosenSubject = input("What subject would you like to do? ")
subjects = ["algebra", "computing"]
for i in range(2):
if ChosenSubject.lower() in subjects:
print("\n")
break
else:
print("\nPlease try again.")
ChosenSubject = input("What subject would you like to do?") #not '=='
if ChosenSubject.lower() in subjects:
print("working")
else:
print("You keep typing in something incorrect.\nPlease restart the program.")
This from the doc:
This method returns a copy of the string in which all case-based
characters have been lowercased.

Why doesn't the second part of this loop work? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
The user should insert a name as an input and then confirm with a yes or no (or any of its derivatives) if the 3 worded name includes a middle name or not. So far, I've gotten the loop to work if the answer is yes; however it keeps looping the question if the answer is no.
The purpose: if the answer is yes, the program will understand its a 3 worded name with a middle name and therefore execute naming combinations with the middle name; if its no, the program will understand its a 3 worded name with a second last name instead of a middle name and therefore execute naming combinations accordingly.
Please note I have exluded a lot of the code for sharing purposes.
What I'm I doing wrong? My question is in regards to the elif part of the loop.
print ('enter name')
providedname = input ()
while providedname != 'quit':
if len(providedname.split())==4:
pass
elif len(providedname.split())==3:
print ('Does the name include a middle name or middle initial? Please type yes or no:')
userinput = input()
if userinput.startswith ('ye' or 'Ye' or 'YE' or 'ya' or 'Ya' or 'YA'):
firstname, middlename, lastname = providedname.split()
elif userinput.startswith ('no' or 'No' or 'NO' or 'na' or 'Na' or 'NA'):
firstname, lastname, secondlastname = providedname.split()
else:
pass
print ('enter name or type quit or q to exit')
providedname=input()
continue
You can't use or like that. It makes sense in English, but it doesn't work in Python. One way to express what you're doing is with a mini for loop along with the any function, like so:
if any(userinput.startswith(string) for string in ['ye', 'Ye', 'YE', 'ya', 'Ya', 'YA']):
It reads almost like English if you shuffle the word order around a bit:
If the user input starts with any of the strings in this list...
Even better is to lowercase the input string first. Then you don't have to check so many combinations.
userinput = input().casefold() # Python 3.3+
userinput = input().lower() # Earlier
if any(userinput.startswith(string) for string in ['ye', 'ya']):
As it happens, startswith can also accept a list of strings. You can actually ditch the all the any() machinery and have simply:
if userinput.startswith(('ye', 'ya')):
(Thanks to #kindall for that tip.)
So, just run this in the interpreter
>>> 'ye' or 'Ye' or 'YE' or 'ya' or 'Ya' or 'YA'
'ye'
Your startswith isn't working the way you think it does.
Other than that, you can shorten that statement up if you lowercase your strings.
Runnable example
while True:
providedname = input ('enter name or type quit or q to exit: ')
if providedname in {'quit', 'q'}:
break
names = providedname.split()
if len(names) == 4:
pass
elif len(names) == 3:
userinput = input('Does the name include a middle name or middle initial? Please type yes or no:')
if userinput[:2].lower() in {'ye', 'ya'}:
firstname, middlename, lastname = names
elif userinput[:2].lower() in {'no' , 'na'}:
firstname, lastname, secondlastname = names
else:
pass
print(firstname, lastname)
It LOOKS as though you're missing the ending ' on the print command inside your elif ('Does the name include....), causing the rest of it to be taken as more text input. Try adding that ' and see if that helps!
First, you could have more complex names, for example, "Oscar De La Hoya", which would skip because the name would have length = 4. Ignoring 'difficult names', the next thing would be to work on cleaning the user's input. I would clean the user's input like so:
userinput = input().lower().strip()
This way you can make is simpler for yourself and also more readable.
Now you can do:
if userinput == 'yes':
firstname, middlename, lastname = providedname.split()
else:
firstname, lastname, secondlastname = providedname.split()
Finally (as the other answer states), if a 'valid' input is given, you'll want to break out of the while loop with a break.
You have 2 main problems (ignoring all the syntax errors assuming that they are just here on your question but your actual code is OK)
First you are never breaking out of the while loop, you must use the break statement anywhere you want to exit the loop.
Second you are overwriting the providedname at the end of the loop, that will leave you without the actual name, the variable will be quit even after the correct name was provided.

Why is this function creating an infinite loop?

Why is this creating an infinite loop? And if it isn't creating one, why does the program freeze up? Not like IDLE stops responding, It just stops like I created a infinite loop and the only thing it does is input(). Try the code out to see what I mean.
(also, tell me if the for's are correct in the comments please)
Accounts = {}
def create_account(x,y,z,a):
global Accounts
Checked = False
while Checked == False:
if x in Accounts:
print("Sorry, that name has already been taken")
print("Please choose a new name")
x = input()
for dictionary in Accounts:
for key in dictionary:
if a in key:
print("Sorry, password is invalid or not avalible")
print("Please choose a new password")
a = input()
Accounts[x] = {"Proggress":y,"Points":z,"Pass":a}
print(Accounts[x])
Your code creates an infinite loop because there is nothing to stop it.
while checked == False will do exactly what it sounds like, it will loop over the code over and over until checked = True OR until you break
break will simply stop the loop, allowing the program to finish.
checked = True will also stop the loop
I think that what you are trying to do is something like this:
This code is untested
Accounts = {}
def create_account(x,y,z,a):
global Accounts
Checked = False
while Checked == False:
if x in Accounts:
print("Sorry, that name has already been taken")
print("Please choose a new name")
x = input()
else:
passwordOk = True
for dictionary in Accounts:
for key in dictionary:
if a in key:
passwordOk = False
break
if not passwordOk:
break
if not passwordOk:
print("Sorry, password is invalid or not avalible")
print("Please choose a new password")
a = input()
else:
Checked = True # this is the important part that you missed
Accounts[x] = {"Proggress":y,"Points":z,"Pass":a}
print(Accounts[x])
Just for you to know, your code can be optimized. I tried to solve your issue by modifying as minimum code as possible, so that you could understand the problem
There are two issues causing this.
As you say,
the print() is before the input(), and the print never outputs, so it doesn't get that far
However, let's take a step back: the print statements are inside the block if x in Accounts:. At the very first line, you set Accounts to be an empty dictionary (Accounts = {}), so no matter what x is, at that point, x in Accounts will never be true - there's nothing in it.
Now, you do have a line that adds items to Accounts:
Accounts[x] = {"Proggress":y,"Points":z,"Pass":a}
However, as other people have pointed out, you'll never get here - it's outside the loop, and the loop never exits because Checked is never set to True, nor is a break called.
Your program then is essentially just going through the same few steps that don't do anything:
Does Checked == False? Yep, continue the loop.
Is x in Accounts? Nope, skip this block.
For every dictionary in Accounts, do some stuff, but Accounts is empty, so I don't need to do anything.
Does Check == False? Yep, continue the loop.

NameError when programming in python

I made a program in python that is supposed to accept a name as user input. It will then check if the name given is contained inside a string that is already given and if it is then the program will print out the telephone next to that name. My code is as follows:
tilefwnikos_katalogos = "Christoforos 99111111: Eirini 99556677: Costas 99222222: George 99333333: Panayiotis 99444444: Katerina 96543217"
check=str(input("Give a name: "))
for check in tilefwnikos_katalogos:
if check=="Christoforos":
arxi=check.find("Christoforos")
elif check=="Eirini":
arxi=check.find("Eirini")
elif check=="Costas":
arxi=check.find("Costas")
elif check=="George":
arxi=check.find("George")
elif check=="Panayiotis":
arxi=check.find("Panayiotis")
elif check=="Katerina":
arxi=check.find("Katerina")
s=check.find(" ",arxi)
arxi=s
y=check.find(":",arxi)
telos=y
apotelesma=tilefwnikos_katalogos[arxi+1:telos]
print(apotelesma)
But when I try to run it, I input the name and then the following message pops up:
Traceback (most recent call last):
File "C:\Users\Sotiris\Desktop\test.py", line 16, in <module> s=check.find(" ",arxi)
NameError: name 'arxi' is not defined
What am I doing wrong?
You're getting your error because arxi isn't getting defined in the first place when then name the user gave is not present on your list.You can fix that by simply adding an unconditional else case to your if/else if bundle as pointed in the comments. But the very way you tackled this problem is faulty, storing data like this in a string is a bad idea, you want to use a dictionary:
phone_catalog = {'Christoforos': 99111111, 'Eirini': 99556677, 'Costas': 99222222, 'George':99333333, 'Panayiotis':99444444, 'Katerina': 96543217}
Also check isn't a very clear variable name, maybe you should try using something better like:
user_name = str(input("Give a name: "))
And now you can do your if/elif condition but replacing it for using dictionary logic and making sure you have a final else, like such:
if user_name in phone_catalog:
print(phone_catalog[user_name])
else:
print("Unknown user")
See how the dictionary made your life much easier and your code cleaner here? Read more on Python Data Structures.
so there are a few things you have overlooked / not going as expected, the first of which is how iterating over strings in python works:
tilefwnikos_katalogos = "Christoforos 99111111: Eirini 99556677: Costas 99222222: George 99333333: Panayiotis 99444444: Katerina 96543217"
for check in tilefwnikos_katalogos:
print(check)
#print(repr(check)) #this shows it as you would write it in code ('HI' instead of just HI)
so check can never be equal to any of the things you are checking it against, and without an else statement the variable arxi is never defined. I'm assuming you meant to use the check from the user input instead of the one in the loop but I'm not sure you need the loop at all:
tilefwnikos_katalogos = "Christoforos 99111111: Eirini 99556677: Costas 99222222: George 99333333: Panayiotis 99444444: Katerina 96543217"
check=str(input("Give a name: ")) #the str() isn't really necessary, it is already a str.
if check=="Christoforos":
arxi=check.find("Christoforos")
elif check=="Eirini":
arxi=check.find("Eirini")
elif check=="Costas":
arxi=check.find("Costas")
elif check=="George":
arxi=check.find("George")
elif check=="Panayiotis":
arxi=check.find("Panayiotis")
elif check=="Katerina":
arxi=check.find("Katerina")
else: raise NotImplementedError("need a case where input is invalid")
s=check.find(" ",arxi)
arxi=s
y=check.find(":",arxi)
telos=y
apotelesma=tilefwnikos_katalogos[arxi+1:telos]
print(apotelesma)
but you could also just see if check is a substring of tilefwnikos_katalogos and deal with other conditions:
if check.isalpha() and check in tilefwnikos_katalogos:
# ^ ^ see if check is within the string
# ^ make sure the input is all letters, don't want to accept number as input
arxi=check.find(check)
else:
raise NotImplementedError("need a case where input is invalid")
although this would make an input of C and t give Cristoforos' number since it retrieves the first occurrence of the letter. An alternative approach which includes the loop (but not calling the variable check!) would be to split up the string into a list:
tilefwnikos_katalogos = "..."
check = input(...)
for entry in tilefwnikos_katalogos.split(":"):
name, number = entry.strip().split(" ")
if check == name:
apotelesma=number
break
else:
raise NotImplementedError("need a case where input is invalid")
although if you are going to parse the string anyway and you may use the data more then once it would be even better to pack the data into a dict like #BernardMeurer suggested:
data = {}
for entry in tilefwnikos_katalogos.split(":"):
name, number = entry.strip().split(" ")
data[name] = number #maybe use int(number)?
if check in data:
apotelesma = data[check]
else:
raise NotImplementedError("need a case where input is invalid")

Output Daffodils-Numbers with python

I want to write a python program, first it asks you to enter two numbers, and then output all daffodil numbers between the two numbers, and it will continue run, until I enter a "q". I write a program, but it is wrong:
#coding=utf-8
while 1:
try:
x1=int(raw_input("please enter a number x1="))
x2=int(raw_input("please enter a number x2="))
except:
print("please enter only numbers")
continue
if x1>x2:
x1,x2=x2,x1
pass
for n in xrange(x1,x2):
i=n/100
j=n/10%10
k=n%10
if i*100+j*10+k==i+j**2+k**3:
print ("%-5d")%n
pass
Can somebody help? I think it should be simple, but I am not able to write it correctly.
I believe you've misunderstood the problem statement. Try this instead:
if i*100+j*10+k==i**3+j**3+k**3:
ref: http://en.wikipedia.org/wiki/Narcissistic_number
for n in xrange(x1,x2):
digits = map(int,str(n))
num_digits = len(digits)
if sum(map(lambda x:x**num_digits,digits)) == n:
print "%d is a magic number"%n
you will still have the issue of not being able to enter "q" since you force the input to be integers
I would like to address the quit event.
while True:
x1 = raw_input("please enter a number x1=")
x2 = raw_input("please enter a number x2=")
quit = ('q','Q')
if x1 in quit or x2 in quit:
break
else:
try:
x1, x2 = int(x1), int(x2)
except:
print("please enter only numbers")
continue
# The mathematical part... (for completeness) (not my code)
if x1>x2:
x1,x2=x2,x1
for n in xrange(x1,x2):
i=n/100
j=n/10%10
k=n%10
if i*100+j*10+k==i+j**2+k**3:
print "%-5d"%n
The pass statement is used only when you don't have anything to be executed in certain block of code. It does nothing more, so don't use it if not needed. It is there for the sake of the code looking clean & with correct indentation.
if some_thing: # don't do anything
else:
some_thing = some_thing_else
Note how the above if statement is syntactically incorrect. This is where pass comes handy. Say, you decide to write the if part later, till then you must provide pass.
if some_thing: # don't do anything
pass
else:
some_thing = some_thing_else
It's a bit tricky to know what's going on without more hints, but some issues I see right off:
You need to be consistent with indentation in Python. Your last if statement is less indented than statements above it (like the previous if and the for loop). This will cause an error. You're also using different amounts of indentation in other places, but since it's not inconsistent that's allowed (if a bad idea). Its usually best to pick one indentation standard (like four spaces) and stick with it. Often you can set your text editor to help you with this (turn on "Expand tabs to spaces" or something in the settings).
You've got two pass statements where they're unneeded or harmful. The first, after the line that has if x1>x2: x1,x2=x2,x1 is going to cause an error. You can't have an indented "suite" of code if you've put a series of simple statements on the end of your compound statement like an if. Either put the assignment on its own line, indented, or get rid of the pass. The last pass at the end of the code is not an error, just unnecessary.
You're missing a colon at the end of your try statement. Every statement in Python that introduces an indented suite ends with a colon, so it should be easy to learn where they're needed.
while True:
x1 = raw_input("please enter a number x1=")
x2 = raw_input("please enter a number x2=")
quit = ('q','Q')
if x1 in quit or x2 in quit:
break
else:
try:
x1, x2 = int(x1), int(x2)
except:
print("please enter only numbers")
continue
if x1>x2:
x1,x2=x2,x1
pass
for n in xrange(x1,x2):
i=n/100
j=n/10%10
k=n%10
if i*100+j*10+k==i+j**2+k**3:
print ("%-5d")%n
pass
i have it! thx to Ashish! it is exactly what i want! and i will quit wenn i enter q! thx a lot!

Categories