Recursion with defined functions for simple text-based Python program - python

I'm writing my first program - it's an idiom generator, which combines individual elements from lists of random verbs, nouns, and pronouns (that I have entered) in Madlibs style and generates a humorous expression. This is a simplified version of my source code:
baseFunction = True
def mainFunction() :
import random
quest = input("Which language do you want it in? Type 'French' or 'English'. ")
if quest == "French" or "french":
verb =
#list of verbs I have manually entered
noun =
#list of nouns I have manually entered
pronoun =
#list of pronouns I have manually entered
morenouns =
#list of nouns I have manually entered
phrase = random.choice(verb) + random.choice(noun) + random.choice(pronoun) + random.choice(morenouns)
print(phrase)
print("Now, give it some meaning and use in the world!")
elif quest == "English" or "english":
verb =
#another list of verbs I have manually entered
noun =
#another list of nouns I have manually entered
pronoun =
#another list of pronouns I have manually entered
morenouns =
#another list of nouns I have manually entered
phrase = random.choice(verb) + random.choice(noun) + random.choice(pronoun) + random.choice(morenouns)
print(phrase)
print("Now, invent some meaning for it and use it in the world!")
f8 = input("Do you want to make another one? Say 'yes' if you do. ")
if f8 == "yes" or "Yes":
mainFunction()
else:
print("Thanks for playing!")
else:
print("Didn't quite catch that. Try again! (say yes!)")
mainFunction()
def malif() :
ques = input("Want to hear a funny idiom? Say 'yes' or 'no'. ")
if ques == "yes" or "Yes":
mainFunction()
elif ques == "no" or "No":
print("Wrong answer. Try again! (say yes)")
malif()
else:
print("Didn't quite catch that. Say 'yes' or 'no'.")
while baseFunction :
malif()
mainFunction()
Essentially, I am asking the user whether they want to make an idiom, offering them a choice of language, generating the expression for them, and then asking them if they want to repeat the process. When I run the script in PyCharm, it runs the two functions in order (meaning, malif() first and then mainFunction(), as I have it at the end) but it does not pay any attention to my input (ex. if I say 'no' it runs the mainFunction anyway and will always do it in French even if I say 'English').
I used some of the tips discussed in this entry (Python - How to make program go back to the top of the code instead of closing). I think the problem lies calling the functions in their own definitions (ex. calling malif() if I answer 'no' to input 'ques', which is defined in malif() ). Yet, I have followed the tips discussed in the question that I linked and it is still not working the way that I want it to. Am I doing something wrong in formatting the code (ex. in terms of indentation) or if it is not obvious what I am doing wrong, is there a way for me to loop functions back to the beginning that was not suggested in the original question?
Thanks!

First some tips when you work with strings as input. Python will make the difference between caps and non-caps letter, thus a good way to deal with strings is to lower() them first (or upper(), ...):
Example:
ques = input("Enter Yes or No: ")
ques = ques.lower()
if ques == "yes":
# do something
elif ques == "no":
# do something else
else:
# raise error
Now I feel like your code is build in a funny way. A good habit is to separate the import from the functions, from the main program. The 2 first will be imported if the module (file) is imported, while the last one will be played when the file is executed. To do so, you can use this:
# -*- coding: utf-8 -*-
"""
docstring of the module
"""
# Imports
import random
import os
# Functions
def f():
return "Hello world"
# Main program
if __name__ == '__main__':
# Calling the function, taking the inputs and so on
In the main program, it's rather useful to deal with the possibility that an exception is raised. Moreover, if you use the cmd to display your program, the cmd will close immediately when an error is raised. This syntax is quite useful:
if __name__ == '__main__':
try:
# Do stuff
except:
import sys
print (sys.exc_info()[0])
import traceback
print (traceback.format_exc())
os.system("pause") # for windows, else easy way is to have an empty input to freeze the cmd
Now your code. I would rework it this way:
# -*- coding: utf-8 -*-
"""
Docstring
"""
# Imports
import random
import os
# Functions
def build_a_phrase(language) :
if language == "french":
verb = ["vendre", "atterir", "attaquer", "jeter"]
#list of verbs I have manually entered
noun = ["arbre", "poisson", "chien"]
#list of nouns I have manually entered
pronoun = ["un", "les"]
#list of pronouns I have manually entered
morenouns = ["chat", "oiseau"]
#list of nouns I have manually entered
choices = [random.choice(verb), random.choice(noun), random.choice(pronoun), random.choice(morenouns)]
phrase = " ".join(choices) # Add a space between the words
return phrase
elif language == "english":
verb = ["...", "...", "..."]
#another list of verbs I have manually entered
noun = ["...", "...", "..."]
#another list of nouns I have manually entered
pronoun = ["...", "...", "..."]
#another list of pronouns I have manually entered
morenouns = ["...", "...", "..."]
#another list of nouns I have manually entered
choices = [random.choice(verb), random.choice(noun), random.choice(pronoun), random.choice(morenouns)]
phrase = " ".join(choices) # Add a space between the words
return phrase
if __name__ == '__main__':
try:
# Parameters
available_language = ["french", "english"]
available_answers = ["yes", "no"]
# Safety implementation of an input
quest = ""
i = 0
while quest.lower() not in available_answers:
quest = input("Want to hear a funny idiom? Say 'yes' or 'no'. ")
i += 1
if i == 2: # number of tries
break
if quest.lower() == "no":
print ("I'm sure you meant yes.")
language = ""
i = 0
while language.lower() not in available_language:
language = input("Which language do you want it in? Type 'French' or 'English'.\n")
i += 1
if i == 2: # number of tries
break
while True:
sentence = build_a_phrase(language)
print (sentence)
print ("Now, give it some meaning and use in the world!")
f8 = ""
i = 0
while f8.lower() not in available_answers:
f8 = input("Do you want to make another one? Say 'yes' if you do. ")
i += 1
if i == 2: # number of tries
break
if f8.lower() == "no":
print("Thanks for playing!")
break
except:
import sys
print (sys.exc_info()[0])
import traceback
print (traceback.format_exc())
os.system("pause")
Hope you'll get a few good tricks from this answer, and some good habits :)
Not complete yet, when the input is wrong, an Error should be raised rather than waiting for the error resulting in the wrong input (i.e. a raise statement should be placed instead of the breaks)

Related

if statement won't execute all of the commands in its indentation level

I am working on a stupid yet funny practice program to improve my understanding of OOP in Python.
The program is meant to randomly generate some band names from a randomly selected adjective and another randomly selected noun - producing a lot of hilarious band names.
For the most part, the program works fine, but for some reason, there are some problems with the if-statements and the while loop in the menu(self)- method all the way down in the BandList class.
My hypothesis is that there is something wrong with the nesting of the else-if statements, or that the loop doesn't manage to advance the loop when I call on the self._generateBand() method in line 60 due to some technicality I'm not aware of. Either way, I'm not sure.
However, my question is:
Why does my loop stop at the line self._writeBand() and not continue executing the code that follows? (As shown below)
done = False
while done != True:
print("\n=============== BAND NAME GENEREATOR ==================")
start = input("\nDo you want to generate a list of bandnames? (y/n): ")
if start.lower() == "y":
self._generateBand()
self._writeBand() #The loop stops here for some reason and asks the same question over and over.
#The program won't execute this part of the code.
inp = ("\nDo you want to save these band names? (y/n): ")
if inp.lower() == "y":
outfile = input("What do you want to name the file?: ")
self._saveBand(f"{oufile}.txt")
If anyone can help me fix this, I would be super grateful.
In advance: Thank you for your help.
The complete program is pasted in below
import random
class Band:
def __init__(self, name):
self._Bandname = name
def __str__(self):
return f"{self._Bandname}"
def hentName(self):
return self._Bandname
class BandList:
def __init__(self):
self._BandList = []
def _readFile(self, filename1, filename2):
with open(filename1) as infile1, open(filename2) as infile2:
lineAdjective = infile1.read().splitlines()
lineNoun = infile2.read().splitlines()
adjective = random.choice(lineAdjective)
noun = random.choice(lineNoun)
return f"{adjective} {noun}"
def _saveBand(self, filename):
with open(filename, "w") as outfile:
for j, i in enumerate(self._BandList):
outfile.write(f"Nr: {j}\t-{i}\n")
def _generateBand(self):
num = int(input("\nHow many band names would you like to generate?: "))
for i in range(num):
bandname = f"The {self._readFile('adjective.txt', 'noun.txt')}s"
self._BandList.append(Band(name= bandname))
def _writeBand(self):
print("\n========= Genererte bandname =========")
for i in self._BandList:
print(i)
#print(i.hentName())
def _deleteBand(self):
self._BandList.clear()
def _writeGoodbyeMsg(self):
print("\n============ PROGRAM TERMINATING ================")
print("\t- thanks for using the program, goodbye!")
def menu(self):
done = False
while done != True:
print("\n=============== BAND NAME GENEREATOR ==================")
start = input("\nDo you want to generate a list of bandnames? (y/n): ")
if start.lower() == "y":
self._generateBand()
self._writeBand() #This is probably where the bug is...
inp = ("\nDo you want to save these band names? (y/n): ")
if inp.lower() == "y":
utfil = input("What do you want to name the file?: ")
self._saveBand(f"{utfil}.txt")
elif inp.lower() == "n":
self._deleteBand()
inp2 = input("Do you want to generate more band names? (y/n)?: ")
if inp2.lower() == "y":
self._generateBand()
elif inp2.lower() == "n":
done = True
self._writeGoodbyeMsg()
else:
print("Unknown command, please try again")
else:
self._writeGoodbyeMsg()
done = True
if __name__ == '__main__':
new = BandList()
new.menu()
You're missing an input call on your 2nd question for saving the band names. It should be:
inp = input("\nDo you want to save these band names? (y/n): ")
It does work. You just haven't given any values in self._BandList.
Its returning "BandList": null.

What kind of unittests make sense for a chatbot in python?

I am creating a simple chatbot at the moment and now I would like to test it using the unittests package. I am new to programming and I have only recently heard about the existence of unit tests. I am thinking that for a chatbot to be tested, it would make sense to see if it gives the right output to the right user input. So if the user writes "Hi", for example, it should output "Hey what's up?" as specified in the library (I have written a bunch of libraries for it; it is not a deep-learning-kinda-bot).
This is the code of my chatbot:
# importing regular expression operations, which we need later to access the libraries
import re
import random # later we want to pick a chatbot answer to a statement by random
from Jordan_Library import dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona # import libraries
# and here we imported all the topic libraries
class JordanBot:
"""
A class that represents the abilities of Jordan the chatbot.
...
Attributes
----------
name : str
user can insert name, which will later be shown in the chat.
Methods
----------
name : str
user can insert name, which will later be shown in the chat.
"""
def __init__(self, name): # this function is always called when a new object of the class is called
"""
Constructing topics for Jordan. Takes the user's name as an argument.
"""
self.name = name
def chat(self):
"""
A function that enables chatting with Jordan after picking a topic.
Take no arguments.
"""
topics = [dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona]
while True:
print("Welcome. My name is Jordan. You can now choose a topic that we can talk about.")
print("Press '0' to have some good old-fashioned smalltalk.")
print("Press '1' to tell me about your deepest sorrows and your destroyed soul.")
print("Press '2' to curse with me.")
print("Press '3' to meditate with me.")
print("Press '4' to talk about Corona.")
# execute this welcome text and tell user what to press in order to pick a topic.
choice = input(">>> ")
# initialize input
if choice == '0': # determine which key to press to initiate the specific topic
print("Alrighty, let's do some chitchatting.")# initiate welcome text for specific topic
print("Don't forget that I am sensitive to punctuation.")
elif choice == '1':
print("Please tell me about your deepest sorrows and your destroyed soul.")
print("Don't forget that I am sensitive to punctuation.")
elif choice == '2':
print("Make yourself ready. let's insult each other!")
print("Don't forget that I am sensitive to punctuation.")
elif choice == '3':
print("Ok then, let's meditate.")
print("Don't forget that I am sensitive to punctuation..")
elif choice == '4':
print("Let's talk about Corona.")
print("Don't forget that I am sensitive to punctuation..")
elif choice == 'q': # if user wants to quit
break
else: # if user pressed the wrong key.
print("Try again.")
edition = topics[int(choice)]
statement = list(map(lambda x:re.compile(x[0], re.IGNORECASE), edition))
# list(map()) applies a function to all elements of a specified object, in this case the cursing library
# lambda makes sure that re.compile is applied in a certain way to all elements of the library without being case-sensitive
# re.compile makes sure that the elemets are turned into objects that can be matched later to another item in the library
answer = list(map(lambda x:x[1], edition))
# same here, but here we pick the second argument in the list x[1], which entails Jordan's answers
while True:
userInput = input(' ' + self.name + ' >>> ') # this allows for the user to type in keys
resp = "I did not understand what you said. Also, I am sensitive to punctuation." # initalize response variable
counter = 0 # initalize counter
while resp == "I did not understand what you said. Also, I am sensitive to punctuation." and counter < len(statement): # determine when to start my loop
for i in range(0, len(statement)): # loop through the indexed library
match = statement[i].match(userInput) # look if input of the user matches with one of the words in the library
if match:
word = statement[i].split(userInput)[1] # We have to take the first element of this list
resp = random.choice(answer[i]) # if there is a match, pick a random answer from x[1]
counter += 1 # make sure that the counter is now + 1 so it does not write the initialized response from the beginning but continues with the loop
# if there is no match though, then it will write the initialized answer
if userInput == 'q':
print(random.choice(answer[i]))
print("---------------------------------")
print("Do you want to choose another topic? Pick below or press 'q' to quit for realsies.")
print("---------------------------------")
break
resp = resp.format(word)
print('____________')
print(' ')
print('Jordan >>> ' + resp) # print Jordan's answer
The unittests I am trying to create are something like:
import unittest
from Jordan_Library import dict_smalltalk, dict_caring, dict_cursing, dict_meditating, dict_corona # import dictionairies
from Jordan_Class import JordanBot
class testJordan(unittest.TestCase):
"""
A class to test several units of Jordan to make sure everything is working correctly.
"""
def test_match(self):
j = JordanBot('StringName')
j.chat()
user_input = "What are you?"
bot_output = list(map(lambda x:x[1], dict_smalltalk)) # this is how I access the right response in the right library
matched = bot_output.match(user_input)
self.assertIn("I am a chatbot, dude. What do you think?", matched)
def test_regex(self):
j = JordanBot('StringName')
j.chat()
text = 'something'
regex = {}
self.assertRegexpMatches(text, regex)
if __name__ == '__main__':
unittest.main()
The main problem is that these lines only run my code, but not really any unit tests. Advice is much appreciated!

How to use a function's if statement to use info from another function?

So I'm designing a sign-in AI, and I want it to work so that the admin name is Shawn. Here is my issue:
The program starts with the interface -
def interface():
username = input('Hello traveler, what is your name? ')
lowerUsername = username.lower()
print()
print()
if lowerUsername == 'megan':
print()
print('So you are the one Shawn has told me so much about? You are looking beautiful today my dear ☺️🌷')
elif lowerUsername == 'shawn':
OfficialSignInEdit()
So you can see at the end that if the user inputs that their name is 'shawn' at sign-in, it calls on the OfficialSignInEdit function, which is the admin sign in. It looks like this:
def OfficialSignInEdit():
print()
if PossInputs('perio', 'What street did you grow up on?: ') == correct:
print()
print('Greetings Master Shawn, it is a pleasure to see you again πŸ™‚')
else:
print()
res1 = input('Incorrect password, try again? (Yes/No)')
lowres1 = res1.lower()
if lowres1 == 'yes':
print()
print()
OfficialSignIn()
elif lowres1 == 'no':
print()
print()
interface()
So I have pinpointed the source of my issue to be right here in this particular line:
if PossInputs('perio', 'What street did you grow up on?: ') == correct:
print()
print('Greetings Master Shawn, it is a pleasure to see you again πŸ™‚')
this (just for your reference) is the PossInputs function:
def PossInputs(x, y):
term = x
question = input(y)
lowQuestion = question.lower()
words = lowQuestion.split()
if term in words:
print()
print (correct)
else:
print()
print (incorrect)
So what I want to happen is, when 'shawn' is entered as a name, the program will jump to the OfficialSignInEdit Function, and ask the question 'What street did you grow up on?'. Then IF the user enters the answer 'perio', the program will print 'correct', and then print the message 'Greetings Master Shawn, it is a pleasure to see you again'. I tried to say that IF PossInputs == correct (and I did define correct = 'correct', and incorrect = 'incorrect' outside all functions) then this would happen, but instead it prints 'correct', and then 'Incorrect password, try again? (Yes/No)', so how can I make a conditional statement that says that if the user answers 'perio', then it will print the welcome message?
Just for thoroughness sake, I also tried
if PossInputs('perio', 'What street did you grow up on?: ') == True
also without success...
anyways anything you can give me is extremely appreciated, if you have any questions or you would like to to clarify something about the written code, I would be more than happy to get back with you as soon as I can.
Thanks!

Nosetests - Guess The Number Game

I am trying to write a few tests for a random input number game but not too sure how to proceed on.
I am following the Python Game from http://inventwithpython.com/chapter4.html
Started the tests with a file test_guess.py
from unittest import TestCase
import pexpect as pe
import guess as g
class GuessTest(TestCase):
def setUp(self):
self.intro = 'I have chosen a number from 1-10'
self.request = 'Guess a number: '
self.responseHigh = "That's too high."
self.responseLow = "That's too low."
self.responseCorrect = "That's right!"
self.goodbye = 'Goodbye and thanks for playing!'
def test_main(self):
#cannot execute main now because it will
#require user input
from guess import main
def test_guessing_hi_low_4(self):
# Conversation assuming number is 4
child = pe.spawn('python guess.py')
child.expect(self.intro,timeout=5)
child.expect(self.request,timeout=5)
child.sendline('5')
child.expect(self.responseHigh,timeout=5)
child.sendline('3')
child.expect(self.responseLow,timeout=5)
child.sendline('4')
child.expect(self.responseCorrect,timeout=5)
child.expect(self.goodbye,timeout=5)
def test_guessing_low_hi_4(self):
# Conversation assuming number is 4
child = pe.spawn('python guess.py')
child.expect(self.intro,timeout=5)
child.expect(self.request,timeout=5)
child.sendline('3')
child.expect(self.responseLow,timeout=5)
child.sendline('5')
child.expect(self.responseHigh,timeout=5)
child.sendline('4')
child.expect(self.responseCorrect,timeout=5)
child.expect(self.goodbye,timeout=5)
and the guess.py file with
intro = 'I have chosen a number from 1-10'
request = 'Guess a number: '
responseHigh = "That's too high."
responseLow = "That's too low."
responseCorrect = "That's right!"
goodbye = 'Goodbye and thanks for playing!'
def main():
print(intro)
user_input = raw_input(request)
print(responseHigh)
print(request)
user_input = raw_input(request)
print(responseLow)
user_input = raw_input(request)
print(responseCorrect)
print(goodbye)
if __name__ == '__main__':
main()
Not sure How to proceed on with writing a few more tests with if statement to test if the value is low or high. I was told to try a command line switch like optparse to pass the number but not sure how to do that either.
Somewhat of a new person with Python, any guidance or assistance would be appreciated.
In order to do command line parsing in nosetests, you'll have to do something similar to this (at least that's what I had to do), i.e. create a plugin that will give you access to the command line parameter in nosetests. Once you had the plugin added which gave you the command line parameter, it would be pretty easy to create a test that would take advantage of that passed in parameter.
from test_args import case_options
class GuessTest(TestCase):
...
def test_guessing(self):
# Conversation assuming number is 4
if case_options.number < 4:
# Do something
elif case_option.number > 4:
# Do some other test
else:
# Do the final test
Does that make sense? I might be misunderstanding what you are trying to do so if I am, just let me know and hopefully we can clear it up.

Python- how to init random function

I am a beginer python learner. I am trying to create a basic dictionary where random meaning of words will come and user have to input the correct word. I used the following method, but random doesn't work. I always get the first word first and when the last word finishes, I get infinite 'none' until I kill it. Using python 3.2
from random import choice
print("Welcome , let's get started")
input()
def word():
print('Humiliate')
a = input(':')
while a == 'abasement':
break
else:
word()
# --------------------------------------------------------- #
def word1():
print('Swelling')
a = input(':')
while a == 'billowing':
break
else:
word()
# ------------------------------------------------------------ #
wooo = [word(),word1()]
while 1==1:
print(choice(wooo))
is there any faster way of doing this and get real random? I tried classes but it seems harder than this. Also, is there any way I can make python not care about weather the input is capital letter or not?
To answer one part of your question ("is there any way I can make python not care about weather the input is capital letter or not?"): use some_string.lower():
>>> "foo".lower() == "foo"
True
>>> "FOO".lower() == "foo"
True
An this is to help you how you could improve the structure of your code:
import sys
from random import choice
WORDPAIRS = [('Humiliate', 'abasement'), ('Swelling', 'billowing')]
def ask():
pair = choice(WORDPAIRS)
while True:
answer = raw_input("%s: " % pair[0]).lower()
if answer == pair[1]:
print "well done!"
return
def main():
try:
while True:
ask()
except KeyboardInterrupt:
sys.exit(0)
if __name__ == "__main__":
main()
It works like that:
$ python lulu.py
Swelling: lol
Swelling: rofl
Swelling: billowing
well done!
Humiliate: rofl
Humiliate: Abasement
well done!
Swelling: BILLOWING
well done!
Humiliate: ^C
$
wooo = [word, word1]
while 1:
print(choice(wooo)())
But in any case it will print you None, cause both of your functions return nothing (None).

Categories