Subclass Method That Generates Different Message If Input Len > 20 - python

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

Related

(Classname) has no attribute (attribute)

I'm trying to create a DnD style dungeon crawler game. I'm using the 5E SRD and other publicly available information as the base for my characters and gameplay.
Currently I'm working on the character generator, and it seems to be going well, but I've hit a roadblock when trying to assign the racial bonuses. I've got the races set up as their own subclasses, each with it's unique bonuses. When I try to assign the appropriate bonuses based on the character's race I get a (Classname)has no attribute (attribute) error.
python
class Race:
def __init__(self, race):
self.name = race
self.racial_str_bonus = 0
self.racial_char_bonus = 0
class Dragonborn(Race):
def __init__(self):
super()
self.name = "Dragonborn"
self.racial_str_bonus = +2
self.racial_char_bonus = +1
def get_racial_bonus(race):
race = race
racial_str_bonus = 0
racial_char_bonus = 0
if race == "Dragonborn":
racial_str_bonus = Dragonborn.racial_str_bonus
racial_char_bonus = Dragonborn.racial_char_bonus
return racial_str_bonus, racial_char_bonus
class BaseCharacter:
def __init__(self, racial_str_bonus, racial_char_bonus):
self.racial_str_bonus = racial_str_bonus
self.racial_char_bonus = racial_char_bonus
#classmethod
def generate_player_character(cls):
cls.race = input("Race: ")
get_racial_bonus(cls.race)
BaseCharacter.generate_player_character()
What I'm looking for is something along the line of:
'''
Race: Dragonborn
print(my_player_char.racial_str_bonus)
2
'''
Where am I goofing up?
Thanks, everyone for the feedback. In cleaning up the code to get it minimally reproducible, I figured out the issue. Per Jonrsharpe's note, I corrected the inhertance invocation to 'super().init(self)'. Once that was correct, I realized that the way they had been defined, I had to include parentheses in the property call: "Dragonborn().racial_str_bonus".
Thanks again, and I will remember to improve my submissions in the future.

Exercism Python Bob

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()

AttributeError: 'list' object has no attribute 'assignmentScores'

I don't understand the meaning of this problem or how to fix it!
I keep getting the problem AttributeError: 'list' object has no attribute 'assignmentScores'
What does this mean? and how do I fix this issue?
My code is:
class Student:
studentName = ""
studentCourse = ""
averageMark = 0
grade = "none"
assignmentScores = [1, 2, 3, 4]
def __init__(self, n, c, a, g,m):
self.studentName = n
self.studentCourse = c
self.averageMark = a
self.grade = g
self.assignmentScores = m
def getName(self):
return self.studentName
def getCourse(self):
return self.studentCourse
def getAverage(self):
return self.averageMark
def getGrade(self):
return self.grade
def getMarks(self):
return self.assignmentScores
def setAverage(self):
mark = self.averageMark
return mark
def setGrade(self):
grade = self.grade
return grade
def setMarks(self):
marks = self.setMarks()
return marks
def addMark(self):
score = list.append(self, self.assignmentScores)
def calculateAverage(self):
if len(self.assignmentScores) > 0:
average = sum(self) / float(len(self.assignmentScores))
return average
else:
return 0
def determineGrade(self):
return 0
print(calculateAverage(assignmentScores))
First, please use 4 spaces for all indentation, it helps a lot. PEP 8 is your friend and will keep everyone friendly and helpful.
As for your problem, after running the code myself and looking at the traceback, it looks like you assigned the self.assignmentScores list to self itself, so when you type self.assignmentScores you are looking up an attribute of self, which is now a list instead of an instance of the class.
This mistake comes from the way you called the method:
calculateAverage(assignmentScores)
This method only requires one argument, which is supposed to be an instance of the class Student, but not only are you calling the method directly from the class instead of from an instance, you are using the assignmentScores list as an argument for the method. This makes it so that the method calculateAverage() replaces self with self.assignmentScores so when you try to check if the list is empty the code is reading it as self.assignmentScore.assignmentScore instead of the intended way.
The way you have the class defined at the moment strongly encourages you to call the method like this.
billy = Student("","",0,"none",[1,2,3,4])
print(billy.calculateAverage())
There is another error standing in your way after you solve this problem, but a good look at the traceback and a careful reading of the relevant code will lead you to the solution. Right now all you need is a better understanding of classes and calling methods work.

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.

Instantiating a unique object every time when using object composition?

As an example, just a couple of dummy objects that will be used together. FWIW this is using Python 2.7.2.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
Hammer = Tool(hammer)
Billy = Student(Hammer)
Tommy = Student(Hammer)
That's probably enough code, you see where I'm going with this. If I call Hammer.break(), I'm calling it on the same instance of the object; if Billy's hammer is broken, so is Tommy's (it's really the same Hammer after all).
Now obviously if the program were limited to just Billy and Tommy as instances of Students, the fix would be obvious - instantiate more Hammers. But clearly I'm asking because it isn't that simple, heh. I would like to know if it's possible to create objects which show up as unique instances of themselves for every time they're called into being.
EDIT: The kind of answers I'm getting lead me to believe that I have a gaping hole in my understanding of instantiation. If I have something like this:
class Foo(object):
pass
class Moo(Foo):
pass
class Guy(object):
def __init__(self, thing):
self.thing = thing
Bill = Guy(Moo())
Steve = Guy(Moo())
Each time I use Moo(), is that a separate instance, or do they both reference the same object? If they're separate, then my whole question can be withdrawn, because it'll ahve to make way for my mind getting blown.
You have to create new instances of the Tool for each Student.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
# Instead of instance, make it a callable that returns a new one
def Hammer():
return Tool('hammer')
# Pass a new object, instead of the type
Billy = Student(Hammer())
Tommy = Student(Hammer())
I'll try to be brief. Well.. I always try to be brief, but my level of success is pretty much random.randint(0, never). So yeah.
Lol. You even failed to be brief about announcing that you will try to be brief.
First, we need to be clear about what "called into being" means. Presumably you want a new hammer every time self.tool = object happens. You don't want a new instance every time, for example, you access the tool attribute, or you'd always a get a new, presumably unbroken, hammer every time you check self.tool.broken.
A couple approaches.
One, give Tool a copy method that produces a new object that should equal the original object, but be a different instance. For example:
class Tool:
def __init__(self, kind):
self.kind = kind
self.broken = False
def copy(self):
result = Tool(self.kind)
result.broken = self.broken
return result
Then in Student's init you say
self.tool = tool.copy()
Option two, use a factory function.
def makehammer():
return Tool(hammer)
class Student:
def __init__(self, factory):
self.tool = factory()
Billy = Student(makehammer)
I can't think any way in Python that you can write the line self.tool = object and have object automagically make a copy, and I don't think you want to. One thing I like about Python is WYSIWYG. If you want magic use C++. I think it makes code hard to understand when you not only can't tell what a line of code is doing, you can't even tell it's doing anything special.
Note you can get even fancier with a factory object. For example:
class RealisticFactory:
def __init__(self, kind, failurerate):
self.kind = kind
self.failurerate = failurerate
def make(self):
result = Tool(self.kind)
if random.random() < self.failurerate:
result.broken = True
if (self.failurerate < 0.01):
self.failurerate += 0.0001
return result
factory = RealisticFactory(hammer, 0.0007)
Billy = Student(factory.make)
Tommy = Student(factory.make) # Tommy's tool is slightly more likely to be broken
You could change your lines like this:
Billy = Student(Tool('hammer'))
Tommy = Student(Tool('hammer'))
That'll produce a distinct instance of your Tool class for each instance of the Student class. the trouble with your posted example code is that you haven't "called the Tool into being" (to use your words) more than once.
Just call Tool('hammer') every time you want to create a new tool.
h1 = Tool('hammer')
h2 = Tool('hammer')
Billy = Student(h1)
Tommy = Student(h2)
Oh wait, I forgot, Python does have magic.
class Student:
def __setattr__(self, attr, value):
if attr == 'tool':
self.__dict__[attr] = value.copy()
else:
self.__dict__[attr] = value
But I still say you should use magic sparingly.
After seeing the tenor of the answers here and remembering the Zen of Python, I'm going to answer my own dang question by saying, "I probably should have just thought harder about it."
I will restate my own question as the answer. Suppose I have this tiny program:
class Item(object):
def __init__(self):
self.broken = False
def smash(self):
print "This object broke."
self.broken = True
class Person(object):
def __init__(self, holding):
self.holding = holding
def using(self):
if self.holding.broken != True:
print "Pass."
else:
print "Fail."
Foo = Person(Item())
Bar = Person(Item())
Foo.holding.smash()
Foo.using()
Bar.using()
The program will return "Fail" for Foo.using() and "Pass" for Bar.using(). Upon actually thinking about what I'm doing, "Foo.holding = Item()" and "Bar.holding = Item()" are clearly different instances. I even ran this dumpy program to prove it worked as I surmised it did, and no surprises to you pros, it does. So I withdraw my question on the basis that I wasn't actually using my brain when I asked it. The funny thing is, with the program I've been working on, I was already doing it this way but assuming it was the wrong way to do it. So thanks for humoring me.

Categories

Resources