Exercism Python Bob - python

I'm learning Python through Exercism.IO, I'm currently on the Bob problem where the object of the problem is as follows:
Bob is a lackadaisical teenager. In conversation, his responses are very limited.
Bob answers 'Sure.' if you ask him a question.
He answers 'Whoa, chill out!' if you yell at him.
He says 'Fine. Be that way!' if you address him without actually saying
anything.
He answers 'Whatever.' to anything else.
So far I've passed a few tests and I'm stuck at a point where it's suppose to return whatever but all the characters are integers, so of course it's not working.
Here's where I'm failing:
def test_only_numbers(self):
self.assertEqual(
'Whatever.', bob.hey('1, 2, 3')
)
All the characters are integers and my test to see if they're yelling looks like this:
def is_yelling(self):
return self.sentence == self.sentence.upper()
Obviously the characters are the same when upper or lower case because they're numbers so the program thinks they're yelling. My question is how can I refactor this program to make it so that when the assertion is all numbers, it won't count it as yelling?
def hey(what):
sentence = SentenceThinker(what)
if sentence.is_silence():
return "Fine. Be that way!"
elif sentence.is_yelling():
return "Whoa, chill out!"
elif sentence.is_question():
return "Sure."
else:
return "Whatever."
class SentenceThinker(object):
def __init__(self, sentence):
self.sentence = sentence
def is_yelling(self):
return self.sentence == self.sentence.upper()
def is_question(self):
return self.sentence.endswith("?")
def is_silence(self):
return not self.sentence

consider using the built-in String method str.isupper()

Related

Python printing an Else statement when not supposed to [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 3 months ago.
Improve this question
I'm having trouble trying to get a game creation exercise to stop printing the else statement (at the bottom of the code block). the idea is, you can navigate from room to room, but if you go in a direction you're not supposed it should tell you. However, it seems to be doing that even when you CAN go somewhere. I'd greatly appreciate any advice.
Code is below:
class Room:
number_of_rooms = 0
def __init__(self, room_name):
self.name = room_name
self.description = None
self.linked_rooms = {}
self.character = None
Room.number_of_rooms = Room.number_of_rooms + 1
def set_description(self, room_description):
self.description = room_description
def get_description(self):
return self.description
def set_name(self, room_name):
self.name = room_name
def get_name(self):
return self.name
def describe(self):
print(self.description)
def set_character(self, new_character):
self.character = new_character
def get_character(self):
return self.character
def describe(self):
print( self.description )
def link_room(self, room_to_link, direction):
self.linked_rooms[direction] = room_to_link
def get_details(self):
print(self.name)
print("--------------------")
print(self.description)
for direction in self.linked_rooms:
room = self.linked_rooms[direction]
print( "The " + room.get_name() + " is " + direction)
def move(self, direction):
if direction in self.linked_rooms:
return self.linked_rooms[direction]
else:
print("You can't go that way")
return self
I would greatly appreciate any advice on this, it's maddening. I just need it to stop printing "You can't go that way" when you can. It actually does work, it just insist on printing it every time you go into a new room as well as when you can't.
This is the code it links to
foyer = Room("foyer")
ballroom = Room("ballroom")
dining_hall = Room("dining hall")
kitchen = Room("kitchen")
foyer.link_room(ballroom, "south")
ballroom.link_room(foyer, "north")
ballroom.link_room(dining_hall, "east")
dining_hall.link_room(ballroom, "west")
dining_hall.link_room(kitchen, "north")
kitchen.link_room(dining_hall, "south")
If you add the following test code to the end of your class (assuming that it is inside a module Room.py):
if __name__ == "__main__":
print("Testing")
# rooms
room1 = Room("Floor")
room2 = Room("Kitchen")
room3 = Room("Living Room")
# link the rooms
room2.link_room(room1, "left")
room3.link_room(room1, "right")
# move
room2.move("up") # not allowed
room3.move("right") # should be possible
Then you can run the test code directly if you execute the module.
Now if you are in room 3 (living room) it is possible to get out on the right.
But if you are in room 2 (kitchen) you can only move to the left.
The test prints "You can't go that way" only if you do a move like room2.move("up") which is correct, because only "left" is allowed. Comment that line and you won't see that message any more.
According to this test, the class is behaving as it should. Note that you could (and should!) also write a unit test from the example I gave, asserting the expected output.
Update:
In your example, allowed moves are:
# allowed
kitchen.move("south")
dining_hall.move("north")
ballroom.move("east")
foyer.move("south")
And examples for not allowed moves are:
# not allowed
kitchen.move("west")
dining_hall.move("east")
ballroom.move("south")
foyer.move("north")
For these you will get "You can't go that way".
Maybe the issue you had was that you were using objects rather than strings as parameter to describe the direction.
For example, this will always fail (printing "You can't go that way"):
foyer.move(ballroom) # param should be a string, not an object
To prevent this kind of error, you could add a check to the move method:
def move(self, direction):
if not isinstance(direction, str):
raise ValueError("Direction needs to be a string!")
if direction in self.linked_rooms:
return self.linked_rooms[direction]
else:
print("You can't go that way")
return self
With that addition, the application will throw an exception, if you pass an object and not a string:
ValueError: Direction needs to be a string!
For interactive testing, you could use this code:
new_dir = "nop"
current_room = foyer
while new_dir != "":
print("current room: " + current_room.name)
print("please enter direction:")
new_dir = input()
if new_dir == "":
print("leaving")
break
new_room = current_room.move(str(new_dir))
if new_room != current_room:
print("moving to: " + new_room.name)
current_room = new_room
For debugging, it might be also helpful if you add the following code to the top of the module:
def Log(func):
def inner(*args, **kwargs):
print(f"Logging {str(func)}: {args[1:]}")
result = func(*args, **kwargs)
print(f"Result: {str(result)}")
return result
return inner
Now you can decorate any of the functions with the #Log attribute like so:
#Log
def __init__(self, room_name):
...
#Log
def move(self, direction):
...
Whenever such a decorated function is called, it will print it to the console, e.g.:
please enter direction:
south
Logging <function Room.move at 0x000001994B97C720>: ('south',)
Result: <main.Room object at 0x000001994B97A150>
moving to: ballroom
current room: ballroom
please enter direction:
If you're done with debugging, you can comment out the attributes (# #Log) to remove the extra print outs.

why are both if and else statement getting executed {python}

i have mentioned in store class method search to return None if the for loop end and still no return and in main i have written only if its a none then print nothing but it still prints "nothing"
i don't think there is any indentation as pointed out in questions of similar type
class book:
def __init__(self,_id,name,tech,price,auth):
self._id=_id
self.name=name
self.tech=tech
self.price=price
self.auth=auth
def _print(self):
print(self._id)
print(self.name)
print(self.tech)
print(self.price)
print(self.auth)
class store:
def __init__(self,bookdb,name='abc'):
self.bookdb=bookdb
self.name=name
def search(self,b_name,book_list):
for i in book_list:
if i.name==b_name:
return i._print()
else:
return None
def discount(self,tech,book_list):
amt=0
for i in book_list:
if i.tech==tech:
amt+=i.price
return amt*(0.9)
if __name__=="__main__":
t = int(input())
b_list=[]
bookdb=dict()
for i in range(t):
_id=int(input())
name=str(input())
tech=str(input())
price=int(input())
auth=str(input())
b_list.append(book(_id,name,tech,price,auth))
bookdb[i]=book(_id,name,tech,price,auth)
title=str(input())
tech=str(input())
store_i=store(bookdb)
if store_i.search(title,b_list)== None:
print('nothing')
else:
store_i.search(title,b_list)
if store_i.discount(tech,b_list)== 0:
print('0.0')
else:
print(store_i.discount(tech,b_list))
**Input**
3
1
TIC
CPP
300
Online
2
CR
JAVA
500
BSwamy
3
BR
JAVA
700
RAJA
TIC
JAVA
Output
1
TIC
CPP
300
Online
nothing
1080.0
output required
1
TIC
CPP
300
Online
1080.0
P.S. bookdb dic doesnt have any use here
The _print() method in class book has no return statement, so python will implicitly return None. Then in the search function, when you do return i._print(), it returns None as well, which is why you see the "nothing" output.
Since you're going to test the result, you might as well make search() return a boolean, here's a suggested edit:
def search(self,b_name,book_list):
for i in book_list:
if i.name==b_name:
i._print()
return True
else:
return False
You have a number of things wrong with your code.
First, if you are going to prompt input from the user, you should provide a message along with the prompt. Something like:
...
name=str(input("Enter Book Name: "))
...
Second, Python is very sensitive to the (visual) structure of your code (i.e. indentation levels). So for example, the "else" to your "if"s must always sit at the same indentation level.
def search(self,b_name,book_list):
for i in book_list:
if i.name==b_name:
return i._print()
else:
return None
Third, looking at this it's obviously not what you want. The return statement must come after the for loop
Fourth, returning i._print() isn't going to return i unless the _print function returns i as its last statement.
With these remarks, you should try
def search(self,b_name,book_list):
for i in book_list:
if i.name==b_name:
i._print()
return i
return None
Also, in general
Try giving your variable names that are descriptive. It was difficult to read and understand your code without using a debugger. You should be able to read it and understand as much as possible what it is doing. So variables like t or unobvious abbreviations should be avoided.
This goes along with prompting the user. If you are going to print things to the console, provide some text to explain what the user is reading. Something like:
...
print("Book Name: ", self.name)
...
There is more (Single Responsibility Principle,...), but this is a "okay" start. And you will learn more as you are moving forward.

Subclass Method That Generates Different Message If Input Len > 20

I'm trying to create a chatbot that can engage in simple conversation with a human. The chatbot needs a subclass, BoredChatbot, that inherits the Chatbot as a superclass, but generates the following message if the user's input is greater than 20 characters long:
“zzz... Oh excuse me, I dozed off reading your essay.”
So far I have:
class Chatbot:
""" An object that can engage in rudimentary conversation with a human. """
def __init__(self, name):
self.name = name
def greeting(self):
""" Returns the Chatbot's way of introducing itself. """
return "Hello, my name is " + self.name
def response(self, prompt_from_human):
""" Returns the Chatbot's response to something the human said. """
return "It is very interesting that you say: '" + prompt_from_human + "'"
# define a class called BoredChatbot
class BoredChatbot(Chatbot):
def bored(self):
""" Returns the Chatbot's response to length > 20 characters"""
if len(prompt_from_human) > 20:
return "zzz... Oh excuse me, I dozed off reading your essay."
else:
return(response)
sally = Chatbot("Sally")
human_message = input(sally.greeting())
print(sally.response(human_message))
This isn't working - it prints:
"It is very interesting that you say: + human_message"
regardless of the length.
I also tried to switch the order of the if statement so that it appears outside of the method.
class BoredChatbot(Chatbot):
def bored(self):
""" Returns the Chatbot's response to length > 20 characters"""
return "zzz... Oh excuse me, I dozed off reading your essay."
sally = BoredChatbot("Sally")
human_message = input(sally.greeting())
if len(human_message) > 20:
print(sally.bored(human_message))
else:
print(sally.response(human_message))
But this gave me an error message:
AttributeError: 'Chatbot' object has no attribute 'bored' on line 31
Why isn't it registering bored from the method within BoredChatbot? Thanks for helping me clear this up - I feel like it's really close.
So, let's look here.
class BoredChatbot(Chatbot):
def bored(self):
""" Returns the Chatbot's response to length > 20 characters"""
if len(prompt_from_human) > 20:
return "zzz... Oh excuse me, I dozed off reading your essay."
else:
return(response)
What is prompt_from_human? How do you get that from bored(self)? Also return(response) is going to throw some error because 1) self.response() is the actual function, but 2) response is not defined either.
So, fixing those problem, I don't really think you need a bored function at all. You should instead override the response function and return the super function in order to keep your object functions consistent.
class BoredChatbot(Chatbot):
def response(self, prompt_from_human):
""" Returns the Chatbot's response to length > 20 characters"""
if len(prompt_from_human) > 20:
return "zzz... Oh excuse me, I dozed off reading your essay."
else:
return super(BoredChatbot, self).response(prompt_from_human)
Then sally = BoredChatBot("Sally"), of course

LPTHW ex. 45, how to return functions from a class in another module?

Trying to make my own RPG character generator for ex 45 in Zed Shaw's LPTHW. Part of the assignment is to make a new class for each 'room' of the program, like WelcomeScreen or ChooseMutations.
Here is the main program.
import rooms
class Program(object):
def __init__(self, start):
self.start = start
def run(self):
next_room_name = self.start
while True:
room = getattr(self, next_room_name)
next_room_name = room()
x = rooms.WelcomeRoom()
Program(x.hello_user())
And here is the rooms file its trying to pull stuff from.
class WelcomeRoom(object):
def __init__(self):
pass
def hello_user(self):
print '*' * 79
print '\n'
print '\t\tWelcome to the'
print '\t\tMetamorphosis Alpha Character & Random Encounter Generator'
print '\t\tProgrammed poorly by Raymond Weiss'
print '\n'
print '*' * 79
raw_input('Please press enter to continue')
return 'get_name'
def get_name(self):
name = raw_input('Hello, whats your name?',
'\n',
':> ')
But when I run the main program in python, it just logs out instead of returning the function get_name() from rooms. Output posted below.
Raymond-Weisss-MacBook-Pro:macgre Raylug$ python macgre.py
*******************************************************************************
Welcome to the
Metamorphosis Alpha Character & Random Encounter Generator
Programmed poorly by Raymond Weiss
*******************************************************************************
Please press enter to continue
Raymond-Weisss-MacBook-Pro:macgre Raylug$
I apologize in advance if my question title isn't exactly what im trying to ask, as a newbie its sometimes hard not knowing what exactly to ask.
You're returning a string, not a function (or function result). You probably want something like:
def hello_user(self):
return self.get_name
or
def hello_user(self):
return self.get_name()
Based on your program, I think you probably want the second one. The difference is that the first one returns the get_name function, whereas the second one returns the results of the get_name function.

Better understanding of __str__ usage

I'm trying to better understand the proper usage of the __str__ function.
Let's say I have a very simple class called Character for use in a game that looks like this:
class Character(object):
""" A game character. """
def __init__(self, name):
self.name = name
self.poisoned = False
self.strength = random.randrange(10, 20)
self.max_strength = self.strength
self.dexterity = random.randrange(10, 20)
self.max_dexterity = self.dexterity
self.hit_points = 100
self.spell_points = 100
self.weapon = []
self.spell = []
self.items = []
self.aura = []
self.xp = 0
Prior to learning about the __str__ function, I would have defined a method of the class called print_stats(self) that would print the character stats via Character.print_stats(). After learning about __str__ though it seemed like this was a function for defining and displaying the statistics of an object, similar to what I would do with print_stats(self)... but in playing with it and learning that the returned value must be a string and not contain integers, it appears my assumption is wrong.
So now my question is what are some examples of good usage of the __str__? Would the example I provide benefit from using that function?
Printing stats is a fine use of __str__(). Simply use string formatting to return a single string value:
def __str__(self):
return ('Name: {name}\n'
'Poisoned: {poisoned}\n'
# etc.
).format(**self.__dict__)
__str__ exists so that you can get a string representation of your object. Note that the builtin print function/statement calls str implicitly:
print 1
is exactly the same as:
print str(1)
which is the same as:
print (1).__str__()
because print calls __str__ implicitly.
Now to your class -- The most natural thing to do is if you would have written:
print self.foo,self.bar,self.baz
You could define __str__ as:
def __str__(self):
return " ".join(str(x) for x in (self.foo,self.bar,self.baz))
Now to print your character's stats, you'd just do:
print character #same as `print str(character)` :)
Usually this is a little limited, so there exists string formatting via .format (or old "sprintf" style formatting using the % operator).
def __str__(self):
return "my {foo} went to the {baz} to buy a {bar}".format(foo=self.foo,
baz=self.baz,
bar=self.bar)
def __str__(self):
return 'Name: %s, Hit points: %d' % (self.name, self.hit_points)
The return value has to be a string, but it can be any string, including one that contains the string representation of integers. A simple example:
def __str__(self):
return "%s (strength: %i)" % (self.name, self.strength)
This might return something like "Arthur (strength: 42)".
In basic use cases, __str__() and a method like print_stats() can pretty safely be interchanged. However, especially for something like a character in a game, you might want to go with print_stats(), as you won't be able to refactor __str__() to do something like print the data to the screen (in the context of a graphical desktop or web application), or to take arguments, which could prove useful for things like this.

Categories

Resources