Is there a way to remove too many if else conditions? - python

I am currently making an interactive system using python, that is able to understand and reply. Hence for this there are lots of conditions for machine to analyze and process. For eg. take the following code(for reference only):
if ('goodbye') in message:
rand = ['Goodbye Sir', 'Jarvis powering off in 3, 2, 1, 0']
speekmodule.speek(rand,n,mixer)
break
if ('hello') in message or ('hi') in message:
rand = ['Wellcome to Jarvis virtual intelligence project. At your service sir.']
speekmodule.speek(rand,n,mixer)
if ('thanks') in message or ('tanks') in message or ('thank you') in message:
rand = ['You are wellcome', 'no problem']
speekmodule.speek(rand,n,mixer)
if message == ('jarvis'):
rand = ['Yes Sir?', 'What can I doo for you sir?']
speekmodule.speek(rand,n,mixer)
if ('how are you') in message or ('and you') in message or ('are you okay') in message:
rand = ['Fine thank you']
speekmodule.speek(rand,n,mixer)
if ('*') in message:
rand = ['Be polite please']
speekmodule.speek(rand,n,mixer)
if ('your name') in message:
rand = ['My name is Jarvis, at your service sir']
speekmodule.speek(rand,n,mixer)
So, is there a way in which I can replace all these if else conditions?? Because there are much more conditions going to be, and it will make the execution slower.

Make a exclusive "if":
if 'goodbye' in message:
rand = ['Goodbye Sir', 'Jarvis powering off in 3, 2, 1, 0']
elif 'hello' in message or 'hi' in message:
rand = ['Wellcome to Jarvis virtual intelligence project. At your service sir.']
elif 'thanks' in message or 'tanks' in message or ('thank you') in message:
rand = ['You are wellcome', 'no problem']
elif message == 'jarvis':
rand = ['Yes Sir?', 'What can I doo for you sir?']
elif 'how are you' in message or 'and you' in message or ('are you okay') in message:
rand = ['Fine thank you']
elif '*' in message:
rand = ['Be polite please']
elif 'your name' in message:
rand = ['My name is Jarvis, at your service sir']
else:
raise NotImplementedError("What to do?")
speekmodule.speek(rand, n, mixer)
With a mapping of RegEx:
mapping = {
r"\bgoodbye\b": ['Goodbye Sir', 'Jarvis powering off in 3, 2, 1, 0'],
r"\bhello\b": ['Wellcome to Jarvis virtual intelligence project. At your service sir.'],
...}
for regex, rand in mapping.items():
if re.search(message, flags=re.I):
break
else:
raise NotImplementedError("What to do?")
speekmodule.speek(rand, n, mixer)
It's up to you to decide.

if/elif/else is a natural way to structure this kind of code in Python. As #imant noted, you may use dict-based approach in case of simple branching, but I see some mildly complex logic in your if predicates, so you'll have to check all predicates in any case and you won't have any performance gains with another code structure.
Though it may be a little bit more readable and easier to maintain if you factor out your predicates and actions like this:
from collections import OrderedDict
def goodbye_p(message):
return 'goodbye' in message
def goodbye_a():
rand = ['Goodbye Sir', 'Jarvis powering off in 3, 2, 1, 0']
# As #Moinuddin Quadri I also assume that your `speek` method
# says random message from a list.
# Otherwise you can use `random.choice` method
# to get a random message out of a list: `random.choice(messages)`.
speekmodule.speek(rand, n, mixer)
def hello_p(message):
return 'hello' in message or 'hi' in message
def hello_a():
rand = ['Wellcome to Jarvis virtual intelligence project. At your service sir.']
speekmodule.speek(rand, n, mixer)
# Use `OrderedDict` instead of `dict` to control order
# of checks and actions.
branches = OrderedDict([
# (predicate as key, action as value)
(goodbye_p, goodbye_a),
(hello_p, hello_a),
])
for predicate, action in branches.items():
if predicate(message):
action_result = action()
# You can add some logic here based on action results.
# E.g. you can return some special object from `goodbye_a`
# and then shut down Jarvis here.
# Or if your actions are exclusive, you can add `break` here.
If all your predicates are the same and contain only substring checks, then it may be more performant to have tuples (e.g. ('hello', 'hi')) as dict keys. Then you can iterate over those keys like this:
for words, action in branches.items():
if any(word in message for word in words):
action()

Use dictionary:
someCollections = {
'goodbye': "Something1",
'hello': "Somthing2",
...
}
speekmodule(someCollections [SomeKey],...)

Firstly create a dict object with the key as tuple of string you want to match in your message and associate it with the value string which your Jarvis is suppose to respond. For example:
jarvis_dict = {
('goodbye',) : ['Goodbye Sir', 'Jarvis powering off in 3, 2, 1, 0'],
('hello',
'hi') : ['Wellcome to Jarvis virtual intelligence project. At your service sir.'],
('thanks',
'tanks',
'thank you') : ['You are wellcome', 'no problem'],
('jarvis',) : ['Yes Sir?', 'What can I doo for you sir?'],
('how are you',
'and you',
'are you okay'): ['Fine thank you'],
('*',) : ['Be polite please'],
('your name',): ['My name is Jarvis, at your service sir']
}
Now iterate each key of you dict to check whether any sub-string is the part of the message and if there is any match, call the speekmodule.speek(rand,n,mixer) function as:
for key, value in jarvis_dict.items():
if any(item in message for item in key):
speekmodule.speek(value, n, mixer)
Note: Here I am assuming that speekmodule.speek(value, n, mixer) in your code is working as there is no information available in your code regarding there declaration. I just replaced your rand with value as it the same list of str returned by the dict which is used in your code.

Related

Weird Bug in Python

I've been recently trying to make a bot in Python but got some weird bugs. When I run my code, I get a weird output. Here's my code:
from random import randint
import random
welcome_message = 'Hello, I\'m Vister, your personal assistant. You can command me to do almost anything a computer can do. So, shall we get started? (yes/no)\n'
user_name_message = 'OK, what should I call you?\n'
setup_complete_message = 'That\'s it for setup!'
prompt_message = 'So, what shall I do for you today?\n'
jokes = ['Why did the student eat his homework? Because his teacher told him that it would be a piece of cake!', 'How do you stop an astronaut’s baby from crying? You rocket!', 'Why was 6 afraid of 7? Because 7, 8, 9.', 'Why did the cow cross the road? Because it wanted to go go to the Moovies!']
def get_user_name(message):
user_name = input(message)
print(f'OK, {user_name}! {setup_complete_message}')
def start(message):
welcome_message_input = input(message)
if welcome_message_input == 'yes':
get_user_name(user_name_message)
elif welcome_message_input == 'no':
print('OK, Exiting Applcation...')
else:
print('Invalid Command. Try "yes" or "no".')
def prompt(message):
command = input(message)
if command == 'tell me a joke':
print(jokes[randint(-1, 3)])
elif command == 'give me a random number':
print(random.randint(0, 1000000))
elif command == 'give me a random number in decimals':
print(random.random())
else:
print('Invalid command. Try "tell me a joke" or "give me a random number".')
start(welcome_message)
prompt(prompt_message)
I can only type one command, and then the program exits. That's not what I want for my program, so can somebody help?
It's because you're calling input multiple times. Each input only looks for one thing. If it doesn't match, it does nothing and the next line asks for more input. Try this:
def prompt(message):
command = input(message)
if command == 'tell me a joke':
print(jokes[randint(-1, 3)])
elif command == 'give me a random number':
print(randint(0, 1000000000))
elif command == 'give me a random number with decimals':
print(random(0, 1000000000))
else:
print('Invalid command. Try "tell me a joke" or "give me a random number".')

Is there a way to include either a user input prompt or a time.sleep() function within a key value pair of a dictionary?

I am working on a Python text RPG, and I'm using dictionaries to give initial information to the player about the area they're visiting. (see code for example). When the player types 'look' or 'examine,' I want the console to print out what I have in the value of the EXAMINATION key. What I'd like to have it do is print a section of text at a time, and either wait for the player to hit enter before continuing or at least wait a couple of seconds before printing the next block. Is there a way to achieve this? Perhaps I'm coming at this from the wrong direction?
import time
import sys
def prompt():
print("\n" + "=========================")
print("What would you like to do?")
player_action = input("> ")
acceptable_actions = ['move', 'go', 'travel', 'walk', 'quit', 'examine', 'inspect', 'interact', 'look']
while player_action.lower() not in acceptable_actions:
print("Unknown action, try again.\n")
player_action = input("> ")
if player_action.lower() == 'quit':
sys.exit()
elif player_action.lower() in ['move', 'go', 'travel', 'walk']:
player_move(player_action.lower())
elif player_action.lower() in ['examine', 'inspect', 'interact', 'look']:
player_examine(player_action.lower())
def player_examine(player_action):
if zonemap[myPlayer.location][SOLVED]:
print("There's nothing more here to examine.")
elif zonemap[myPlayer.location][EXAMINATION]:
slowprint(zonemap[myPlayer.location][EXAMINATION])
ZONENAME = ''
DESCRIPTION = 'description'
EXAMINATION = 'examine'
SOLVED = False
UP = 'up', 'north'
DOWN = 'down', 'south'
LEFT = 'left', 'west'
RIGHT = 'right', 'east'
zonemap = {
'Fields': {
ZONENAME: "Western Fields",
DESCRIPTION: "A grassy field to the west of town.",
EXAMINATION: "The grass in this field is extremely soft." + input("> ") + "The wind feels cool on your face." + time.sleep(2) + "The sun is beginning to set.",
SOLVED: False,
UP: "The Mountains",
DOWN: "The Town",
LEFT: "",
RIGHT: "The Ocean",
},
When trying to use the time.sleep() method, I get the following error:
TypeError: can only concatenate str (not "NoneType") to str
When trying to use the input("> ") function, the text simply prints on without waiting.
Your approach doesn't work because you are immediately calling the input() or time.sleep() functions as you build the dictionary. time.sleep(), for example, returns None and that's why you get your error.
You need to call those functions later on, when you retrieved the value from the dictionary and actually want to 'slow print' the description.
You can do so in loads of different ways. You could
use a sequence (such as a list or a tuple) of strings instead of a single string, and have your slowprint() function accept a sequence and pause after printing each element.
use a sequence of strings and mix in special values that slowprint() looks for to do different things, like sleeping or asking for input.
Store a function in the dictionary, that you then call. Functions are objects too, just like strings. The function would handle all the printing and pausing.
E.g. storing a tuple of strings:
EXAMINATION: (
"The grass in this field is extremely soft.",
"The wind feels cool on your face.",
"The sun is beginning to set.",
)
then have your slowprint() function handle that:
def slowprint(lines):
"""Print each line with a pause in between"""
for line in lines:
print(line)
input("> ") # or use time.sleep(2), or some other technique
The second option, to insert special values, gives you the most power to delegate all sorts of extra functionality to other code. You'd need to test for the type of object in the sequence, but this would let you insert arbitrary actions in your examination description. Like the difference between sleeping and asking for the user to hit a key:
class ExaminationAction:
def do_action(self):
# the default is to do nothing
return
class Sleep(ExaminationAction):
def __init__(self, duration):
self.duration = duration
def do_action(self):
time.sleep(self.duration)
class Prompt(ExaminationAction):
def __init__(self, prompt):
self.prompt = prompt
def do_action(self):
return input(self.prompt)
and have the slowprint() function look for these instances:
def slowprint(examine_lines):
for action_or_line in examine_lines:
if isinstance(action_or_line, ExamineAction):
# special action, execute it
action_or_line.do_action()
else:
# string, print it
print(action_or_line)
You can make any number such actions; the point is that they all subclass ExamineAction so can be distinguished from plain strings. Put them into your sequence for the EXAMINATION key:
EXAMINATION: (
"The grass in this field is extremely soft.",
Prompt("> "),
"The wind feels cool on your face.",
Sleep(2),
"The sun is beginning to set.",
)
The possibilities are endless.

Python: check if a dictionary item is in string, then return the match

I have a database table that looks like the following:
id phrases message
1 "social media, marketing" "This person is in marketing!"
2 "finance, fintech "This person is in finance!"
I've looped through the key phrases and added them to a dictionary, like so:
messages = self.db_access.execute("SELECT * FROM messages")
messages = self.db_access.fetchall()
print(len(messages))
if self.db_access.rowcount > 0:
has_message = True
phrase_list = {}
for the_rule in messages:
print(the_rule[1])
rule = the_rule[1].split(',')
for phrase in rule:
phrase = str(phrase)
phrase_list[phrase] = str(the_rule[2])
print(phrase_list)
print("\n")
else:
has_message = False
This then produces the following:
# phrase_list
{'social media': 'This person is in marketing!',
'marketing': 'This person is in marketing!',
'finance': 'This person is in finance!',
'fintech': 'This person is in finance!'}
So, each phrase has it's own accompanying message, which is used elsewhere.
Now, I can compare those dict keys against a string, like so:
descriptions = ["I am in marketing, and it is super awesome", "I am in finance, and it is super awesome"]
My next step is to compare that string with the keys, and if it contains any of the keywords, then print the matching key and its value/message. This is what I have so far:
for description in descriptions:
print(description)
if has_message == True:
if any(x in description for x in phrase_list):
# print matching keyword and message
else:
print("No matches, but a phrase list exists")
So my question is, what do I need to replace that comment with to output 1) the keyword it matched with, and 2) the message associated with that keyword?
You just have to restructure your code a bit. The need for that arouses from the use of any which does not return information on which x made the expression evaluate as True. It just tells you that someone did or that noone did. If you do care about which one you have to loop through or possibly use next. Anyway, here is one way to do it:
for description in descriptions:
print(description)
if has_message == True:
for x in phrase_list:
if x in description:
print(x, phrase_list[x])
break
else:
print("No matches, but a phrase list exists")
Note:
if the else on the for is confusing, just remove it. The code will reach it only if the x is not in any description anyway.
Might want to tweak it a bit, but you can use a regular expression to search for a matching key, then look that back up in your dictionary, eg:
import re
phrase_list = {'social media': 'This person is in marketing!',
'marketing': 'This person is in marketing!',
'finance': 'This person is in finance!',
'fintech': 'This person is in finance!'}
descriptions = ["I am in marketing, and it is super awesome", "I am in finance, and it is super awesome", 'john smith']
def find_department(lookup, text):
m = re.search('|'.join(sorted(lookup, key=len, reverse=True)), text)
if m:
return lookup[m.group(0)]
else:
return 'This person is a mystery!'
Then running this gives you:
for desc in descriptions:
print(desc, '->', find_department(phrase_list, desc))
#I am in marketing, and it is super awesome -> This person is in marketing!
#I am in finance, and it is super awesome -> This person is in finance!
#john smith -> This person is a mystery!

Why is my string recognition algorithm not detecting the correct string?

Apparently, my string recognition algorithm is not working properly. It is returning the wrong responses based on the user's input. I know it's probably something simple, but I just can't see what it is right now. I done the research and I haven't found another python issue here where when the user input is entered, the wrong response is returned. Are my if statements properly formed? Is there an issue with the string search?
import random
def po_response():
response = {1 : 'This is random response A from po_response()',
2 : 'This is random response B from po_response()',
3 : 'This is randomw response C from po_response()'}
answer = random.sample(response.items(),1)
print(answer)
main()
def greetings_response():
response = {1 : 'This is response A from greetings_response()',
2 : 'This is response B from greetings_response()',
3 : 'This is response C from greetings_response()'}
answer = random.sample(response.items(),1)
print(answer)
return response
main()
def main():
userRequest = input('Welcome, Please enter your request. \n')
userReq = userRequest.lower()
if 'how are you' or 'how\'s it going' in userReq:
print('first if')
print('The value of the input is ')
print(userReq)
greetings_response()
elif 'ship' + 'po' or 'purchase order' in userReq:
print('elif')
print('The value of the input is ')
print(userReq)
po_response()
else:
print('Please re-enter your request')
main()
Here is the response I get when I enter 'ship po'
Welcome, Please enter your request.
>>>ship po
first if
The value of the input is
ship po
[(2, 'This is response B from greetings_response()')]
It should not go to the greetings_response() function, it should go to the po_response() function. Not sure why it's acting this way.
Test using or seems wrong. Maybe you meant if ('how are you' in userReq) or ('how\'s it goind' in userReq) ? Have a look to Python operators precedence.

Checking if string contains values of nested lists - python

First of all, do you know why this code doesn't work at all? When I give it the input 'when was steve jobs born' it returns nothing. Secondly I'm almost certain this can be written in a more efficent way which would take less time for the program to execute. Any ideas? Thanks!
import sys
Bill_Gates = ["bill gates","1955", "Co-founder of Microsoft"]
Steve_Jobs = ["steve jobs","1955", "Co-Founder of Apple"]
Albert_Einstein = ["albert einstein","1879", "Phycisist"]
PEOPLE = [Bill_Gates, Steve_Jobs, Albert_Einstein]
userInput = input("say something")
#checking if userInput contains the peoples name
if userInput in [j for i in PEOPLE for j in i]:
for i in range(len(PEOPLE)):
if PEOPLE [i][0] in userInput:
if "when was" in userInput:
if "born" in userInput:
print(PEOPLE[i][0] + "was born in " + PEOPLE[i][1])
UPDATE:
Ahsanul Haque gave me just the answer I was searching for.
You should try to debug these kinds of problems yourself by breaking down what happens at each step. Printing the variables at each stage or playing around with a shell or debugger such as pdb are very helpful. In this case:
>>> Bill_Gates = ["bill gates","1955", "Co-founder of Microsoft"]
>>> Steve_Jobs = ["steve jobs","1955", "Co-Founder of Apple"]
>>> Albert_Einstein = ["albert einstein","1879", "Phycisist"]
>>>
>>> PEOPLE = [Bill_Gates, Steve_Jobs, Albert_Einstein]
>>> things = [j for i in PEOPLE for j in i]
>>> things
['bill gates', '1955', 'Co-founder of Microsoft', 'steve jobs', '1955', 'Co-Founder of Apple', 'albert einstein', '1879', 'Phycisist']
>>> 'steve jobs' in things
True
>>> 'when was steve jobs born' in things
False
So the if userInput in [j for i in PEOPLE for j in i] fails because the right hand side is just a list of strings and Python isn't magic.
Your code is almost there anyway, as it works without the initial check. So this works:
for person in PEOPLE:
if person[0] in userInput and "when was" in userInput and "born" in userInput:
print(person[0] + " was born in " + person[1])
Note that a direct for loop works in this situation; there is no need to use range and index manually. Using the and operator is also cleaner than nested if statements.
This is plenty efficient as is unless PEOPLE has some tens of millions of elements. If you get to that stage you can look into search engine indexing techniques.
'when was steve jobs born' isn't in that list you made, whatever it is; since the outer if fails, nothing inside it gets executed.
What about making a Person class? It's simple, readable and easy to manage.
For example, I've written a Person class with a get_role method. Write your own get_name() and get_year_of_birth method.
class Person:
def __init__(self, name, year_of_birth, role):
self.name = name
self.year_of_birth = year_of_birth
self.role = role
def get_role(self):
return self.role
Bill_Gates = Person("bill gates","1955", "Co-founder of Microsoft")
Steve_Jobs = Person("steve jobs","1955", "Co-Founder of Apple")
Albert_Einstein = Person("albert einstein","1879", "Phycisist")
person_list= [Bill_Gates,Steve_Jobs,Albert_Einstein]
for person in person_list:
print person.get_role()
Output:
Co-founder of Microsoft
Co-Founder of Apple
Phycisist

Categories