I'm new to programming and have an assignment I've been working at for awhile. I understand defining functions and a lot of the basics but I'm kind of running into a brick wall at this point.
I'm trying to figure this one out and don't really understand how the 'class' feature works yet. I'd appreciate any help with this one; also any help with some python resources that have can dummy down how/why classes are used.
You've been going to work on a database project at work for sometime now. Your boss encourages you to program the database in Python. You disagree, arguing that Python is not a database language but your boss persists by providing the source code below for a sample telephone database.
He asks you to do two things:
Evaluate the existing source code and extend it to make it useful for managers in the firm. (You do not need a GUI interface, just work on the database aspects: data entry and retrieval - of course you must get the program to run or properly work
He wants you to critically evaluate Python as a database tool.
Import the sample code below into the Python IDLE and enhance it, run it and debug it. Add features to make this a more realistic database tool by providing for easy data entry and retrieval.
import shelve
import string
UNKNOWN = 0
HOME = 1
WORK = 2
FAX = 3
CELL = 4
class phoneentry:
def __init__(self, name = 'Unknown', number = 'Unknown',
type = UNKNOWN):
self.name = name
self.number = number
self.type = type
# create string representation
def __repr__(self):
return('%s:%d' % ( self.name, self.type ))
# fuzzy compare or two items
def __cmp__(self, that):
this = string.lower(str(self))
that = string.lower(that)
if string.find(this, that) >= 0:
return(0)
return(cmp(this, that))
def showtype(self):
if self.type == UNKNOWN: return('Unknown')
if self.type == HOME: return('Home')
if self.type == WORK: return('Work')
if self.type == FAX: return('Fax')
if self.type == CELL: return('Cellular')
class phonedb:
def __init__(self, dbname = 'phonedata'):
self.dbname = dbname;
self.shelve = shelve.open(self.dbname);
def __del__(self):
self.shelve.close()
self.shelve = None
def add(self, name, number, type = HOME):
e = phoneentry(name, number, type)
self.shelve[str(e)] = e
def lookup(self, string):
list = []
for key in self.shelve.keys():
e = self.shelve[key]
if cmp(e, string) == 0:
list.append(e)
return(list)
# if not being loaded as a module, run a small test
if __name__ == '__main__':
foo = phonedb()
foo.add('Sean Reifschneider', '970-555-1111', HOME)
foo.add('Sean Reifschneider', '970-555-2222', CELL)
foo.add('Evelyn Mitchell', '970-555-1111', HOME)
print 'First lookup:'
for entry in foo.lookup('reifsch'):
print '%-40s %s (%s)' % ( entry.name, entry.number, entry.showtype() )
print
print 'Second lookup:'
for entry in foo.lookup('e'):
print '%-40s %s (%s)' % ( entry.name, entry.number, entry.showtype() )
I'm not sure if I'm on the right track but here is what I have so far:
def openPB():
foo = phonedb()
print 'Please select an option:'
print '1 - Lookup'
print '2 - Add'
print '3 - Delete'
print '4 - Quit'
entry=int(raw_input('>> '))
if entry==1:
namelookup=raw_input('Please enter a name: ')
for entry in foo.lookup(namelookup):
print '%-40s %s (%s)' % (entry.name, entry.number, entry.showtype() )
elif entry==2:
name=raw_input('Name: ')
number=raw_input('Number: ')
showtype=input('Type (UNKNOWN, HOME, WORK, FAX, CELL): \n>> ')
for entry in foo.add(name, number, showtype): #Trying to figure out this part
print '%-40s %s (%s)'% (entry.name, entry.number, entry.showtype() )
elif entry==3:
delname=raw_input('Please enter a name to delete: ')
# #Trying to figure out this part
print "Contact '%s' has been deleted" (delname)
elif entry==4:
print "Phone book is now closed"
quit
else:
print "Your entry was not recognized."
openPB()
openPB()
Learn Python the Hard Way, Dive into Python, and the built in Python Tutorial are all pretty good resources for someone starting to learn Python. I also used Beginning Python when I started learning.
Related
I have a program that I want to be able to print all of the instances of each variable using my method that I created. Problem is I can't figure out a way to print them since each are listed under a different variable that aren't configured from hardcoding them in and I need a way to automatically recall them in my code.
class fit:
def __init__(self,day,did,workout='Not Recorded',time='An unknown amount of',calories='An unknown amount of'):
self.day = day
self.did = did
if did.lower()=='no':
self.workout = 'Not Recorded'
self.time = "An unknown amount of Minutes"
self.calories = "An unknown amount of Calories"
else:
self.workout = workout
self.time = "{} Minutes".format(time)
self.calories = "{} Calories".format(calories)
def formate(self):
self.formate = "{}:\n\nDid you work out: {}\nWorkout focus: {}\nYou worked out for: {}\nYou burned: {}\n\n----------------------------------------------------------".format(self.day,self.did,self.workout,self.time,self.calories)
return self.formate
def reader(day,index):
file = open('readme.txt')
file = file.read()
stripped = file.rsplit("\n")
for i in range(len(stripped)):
stripped[i] = stripped[i].rsplit(" ")
del stripped[-1]
if int(index) >= len(stripped[day-1]):
return "none"
else:
return stripped[day-1][index]
x = 0
def create_new_instance(class_name,instance_name):
globals()[instance_name] = class_name(reader(x,0),reader(x,1),reader(x,2),reader(x,3),reader(x,4))
print('Class instance {} created'.format(instance_name))
while True:
try:
x+=1
ins = 'day_' + str(x)
create_new_instance(fit,ins)
except:
break
break
def printer(instance):
print(.formate())
while True:
x+=1
inst = 'day_' + str(x)
printer(inst)
An example of this might be that I have 8 lines of data from a text document and I have a system that creates instances of day_1, day_2, day_3 ect until day_8 and then I want to print each of those instances out, but again I don't have those instances directly hardcoded into my code so I don't know how I'd do it. I've tried looking into maybe a while loop and increasing a variable by 1 and concatenating it with day and trying to make a variable out of that but the my limited experience with python isn't helping.
A very unpythonic and ugly way would be to use exec, for example:
day_3=5
x = 'day_'+'3'
exec("print("+x+")")
I would recommend another way to store your variables though.
Let's say I have a spell named heal. How can I prevent a user from spamming heal every time they are damaged. I have considered applying this to individual combat functions; however, I am not sure how to implement a global rule for this? This code may clear it up:
available_spells = ['fireball', 'heal']
equipped = {'Weapon': "Staff",
'Armor': "Robes",
'Spells': ['fireball', 'heal']}
print "Your available spell(s) is(are) '%s'. " % equipped["Spells"]
inp = raw_input("Type the name of a spell you want to use.: ").lower()
lst = [x for x in available_spells if x.startswith(inp)]
if len(lst) == 0:
print "No such spell"
print ' '
elif len(lst) == 1:
spell = lst[0]
print "You picked", spell
#COMBAT FUNCTIONS HERE
else:
print "Which spell of", equipped["Spells"], "do you mean?"
If I were to make a class that defines certain actions for spells to take, how could I implement that into the code I have? For example if I have a class of spells, with functions defining damage rules, cool down times, etc., how could I reference that function in the code I already have? i.e. the player types 'heal' and I want it to reference an above class that has those values defined to check if the player recently played the spell, and what it does when played.
Am I clear enough in this question? How should I write a spell cool-down mechanic? How can I implement this mechanic into the code above?
Instead of storing all available spells as a list, you could store them as a dictionary, which allows you to also store the desired cooldown duration:
available_spells = {
# spell name: cooldown duration in seconds
'fireball': 3.0,
'heal': 5.0,
}
Each player could have another dict that keeps track of the last time they cast each spell. When the game starts, it would be empty:
cast_spells = {}
When the player attempts to cast a spell, check if the spell name is in the cast_spells dict. If it's not, then they have not yet cast it this game, so they are allowed to cast it:
if spell_name not in cast_spells:
cast_spells[spell_name] = datetime.now()
Otherwise, if the spell name is in the cast_spells dict, check if the required cooldown has elapsed:
elif cast_spells[spell_name] + datetime.timedelta(seconds=spells[spell_name]) < datetime.now():
cast_spells[spell_name] = datetime.now()
Otherwise, the cooldown is still in effect.
else:
print 'Spell not ready.'
I would probably do it using with, an exception handler, and a simple timer. That way you can just repeat the cooldown pattern, have shared cooldowns (like shown below), or even global cooldowns, etc.
Here are the classes:
import time
class CooldownException(Exception):
pass
class Cooldown(object):
def __init__(self, seconds):
self.seconds = seconds
self.expire = None
def __enter__(self):
if not self.expire or time.time() > self.expire:
self.expire = time.time() + self.seconds
else:
raise CooldownException('Cooldown not expired!')
def __exit__(self, type, value, traceback):
pass
heal_cooldown = Cooldown(5)
def heal():
try:
with heal_cooldown:
print 'You heal yourself!'
except CooldownException as e:
print e
def apply_bandage():
try:
with heal_cooldown:
print 'You bandage yourself!'
except CooldownException as e:
print e
def drink_potion():
try:
with heal_cooldown:
print 'You quaff a potion!'
except CooldownException as e:
print e
And here's how they're used:
>>> heal()
You heal yourself!
>>> time.sleep(3)
>>> drink_potion()
Cooldown not expired!
>>> time.sleep(3)
>>> apply_bandage()
You bandage yourself!
If I were to make a class that defines certain actions for spells to take, how could I implement that into the code I have?
As you guessed, your problem is very well suited to classes.
Am I clear enough in this question?
Yes.
Your program, but with classes
Here is your program modified to use two custom classes, FireballSpell and HealSpell. Each one has a .name, which is a string, and a .cast(), which is a custom behaviour. It's nearly identical to your original code, so it should be easy for you to understand:
available_spells = [FireballSpell(), HealSpell()]
equipped = {'Weapon': "Staff",
'Armor': "Robes",
'Spells': [FireballSpell(), HealSpell()]}
while True:
print "Your available spell(s) is(are) '%s'. " % [spell.name for spell in equipped["Spells"]]
inp = raw_input("Type the name of a spell you want to use.: ").lower()
lst = [spell for spell in available_spells if spell.name.startswith(inp)]
if len(lst) == 0:
print "No such spell"
print ' '
elif len(lst) == 1:
spell = lst[0]
print "You picked", spell.name
spell.cast()
else:
print "Which spell of", [spell.name for spell in equipped["Spells"]], "do you mean?"
print ""
Run it and give it a try! Here is the complete script. I'm pretty sure it does exactly what you want.
Specific spells
Each specific class has a name, cooldown time, and specific behaviour. The parent Spell class (see bottom) handles the rest.
class FireballSpell(Spell):
def __init__(self):
self.name = "fireball"
self.cooldown_seconds = 5
def spell_specific_behaviour(self):
# do whatever you like with fireball
# this is only called if the spell has cooled down
print "casting fireball"
class HealSpell(Spell):
def __init__(self):
self.name = "heal"
self.cooldown_seconds = 10
def spell_specific_behaviour(self):
# same applies here as from FireballSpell
print "casting heal"
Spell class
This is a generic Spell class - the parent of all spells. It knows the name, cooldown time, and behaviour from the specific spells (child classes above). It also has generic cooldown mechanic that's shared by the spells:
class Spell:
# spell data - filled in by specific spells
name = "default"
cooldown_seconds = 0
last_cast_time = 0
def cast(self):
# only cast the spell if it has cooled down
if self.is_cooled_down():
# call the behaviour set by the specific spell
self.spell_specific_behaviour();
# set the last cast time to the current time
self.last_cast_time = time.time()
else:
print self.name + " has not cooled down yet!"
def spell_specific_behaviour(self):
# implement in specific spell subclasses
return
def is_cooled_down(self):
current_time_seconds = time.time()
cooldown_expire_time_seconds = self.last_cast_time + self.cooldown_seconds
return current_time_seconds > cooldown_expire_time_seconds
Again, here is the whole thing in one working script. Have fun!
META: decorators, exceptions, and with blocks? Whoa, guys. OP is just now learning about classes. Let's keep it simple here.
Here is another example using decorators...
from functools import wraps
class Cooldown(object):
def __init__(self, seconds, cooldown_message):
self.seconds = seconds
self.expire = None
self.cooldown_message = cooldown_message
def decorator(self, fail_message_callback):
def _wrap_decorator(foo):
def _decorator(*args, **kwargs):
if not self.expire or time.time() > self.expire:
self.expire = time.time() + self.seconds
result = foo(*args, **kwargs)
return result
else:
if fail_message_callback:
fail_message_callback(self.cooldown_message)
return None
return wraps(foo)(_decorator)
return _wrap_decorator
heal_cooldown = Cooldown(5, 'Cooldown not expired!')
def display(message):
print message
#heal_cooldown.decorator(display)
def heal():
display('You heal yourself!')
#heal_cooldown.decorator(display)
def apply_bandage():
display('You bandage yourself!')
#heal_cooldown.decorator(display)
def drink_potion():
display('You quaff a potion!')
heal()
time.sleep(3)
drink_potion()
time.sleep(3)
apply_bandage()
I'm trying to keep this as simple as possible. Basically I want the data to be saved to a file, and the retrieve it so that questor.py works and can "remember" everything it was ever taught on your machine. The original code is available on the web at http://www.strout.net/info/coding/python/questor.py
If I'm reading the code right, you end up with an object that looks something like {key:{key:{key:class instance},class instance},class instance} . (rough estimate)
Please ignore the unfished method Save, I'm working on that as soon as I figure out how to pickle the dictionary without losing any of the imbedded instances.
The following is my attempt at trying to save the dict via pickler. Some of the code is unfinished, but you should be able to get an idea of what I was trying to do. So far all I am able to do is retrieve the last question/answer set. Either my pickle isn't saving the imbedded instances, or they're not actually there when I save the pickle. I've followed the spaghetti lines as much as possible, but can't seem to figure out how to set up a way to save to file without losing anything.
Also my file doesn't have to be .txt originally I was going to use .data for the pickle.
# questor.py
# define some constants for future use
kQuestion = 'question'
kGuess = 'guess'
questfile = 'questfile.txt'
## Added
import cPickle as p
# create a file for questor
def questor_file():
try:
questor = open(questfile,'rb')
try:
q = p.Unpickler(questor)
quest = q.load()
questor.close()
return quest
except:
print 'P.load failed'
except:
print 'File did not open'
questor = open('questfile.data', 'wb')
questor.close()
return Qnode('python')
# define a function for asking yes/no questions
def yesno(prompt):
ans = raw_input(prompt)
return (ans[0]=='y' or ans[0]=='Y')
# define a node in the question tree (either question or guess)
class Qnode:
# initialization method
def __init__(self,guess):
self.nodetype = kGuess
self.desc = guess
##Added
## Not sure where I found this, but was going to attempt to use this as a retreival method
## haven't gotten this to work yet
def Load(self):
f = open(self.questfile,'rb')
tmp_dict = cPickle.load(f)
f.close()
self.__dict__.update(tmp_dict)
##Added
# was going to use this as a save method, and call it each time I added a new question/answer
def Save(self,node):
f = open(self.questfile,'wb')
quest = p.pickler(f)
# get the question to ask
def query(self):
if (self.nodetype == kQuestion):
return self.desc + " "
elif (self.nodetype == kGuess):
return "Is it a " + self.desc + "? "
else:
return "Error: invalid node type!"
# return new node, given a boolean response
def nextnode(self,answer):
return self.nodes[answer]
# turn a guess node into a question node and add new item
# give a question, the new item, and the answer for that item
def makeQuest( self, question, newitem, newanswer ):
# create new nodes for the new answer and old answer
newAnsNode = (Qnode(newitem))
oldAnsNode = (Qnode(self.desc))
# turn this node into a question node
self.nodetype = kQuestion
self.desc = question
# assign the yes and no nodes appropriately
self.nodes = {newanswer:newAnsNode, not newanswer:oldAnsNode}
self.save(self.nodes)
def traverse(fromNode):
# ask the question
yes = yesno( fromNode.query() )
# if this is a guess node, then did we get it right?
if (fromNode.nodetype == kGuess):
if (yes):
print "I'm a genius!!!"
return
# if we didn't get it right, return the node
return fromNode
# if it's a question node, then ask another question
return traverse( fromNode.nextnode(yes) )
def run():
# start with a single guess node
# This was supposed to assign the data from the file
topNode = questor_file()
done = 0
while not done:
# ask questions till we get to the end
result = traverse( topNode )
# if result is a node, we need to add a question
if (result):
item = raw_input("OK, what were you thinking of? ")
print "Enter a question that distinguishes a",
print item, "from a", result.desc + ":"
q = raw_input()
ans = yesno("What is the answer for " + item + "? ")
result.makeQuest( q, item, ans )
print "Got it."
# repeat until done
print
done = not yesno("Do another? ")
# Added
# give me the dictionary
return result
# immediate-mode commands, for drag-and-drop or execfile() execution
if __name__ == '__main__':
print "Let's play a game."
print 'Think of something, just one thing.'
print 'It can be anything, and I will try to guess what it is.'
raw_input('Press Enter when ready.')
print
questdata = run()
print
# Added
# Save the dictionary
questor = open(questfile,'wb')
q = p.Pickler(questor)
q.dump(questdata)
questor.close()
raw_input("press Return>")
else:
print "Module questor imported."
print "To run, type: questor.run()"
print "To reload after changes to the source, type: reload(questor)"
# end of questor.py
one way that comes to mind is creating a list of all the nodes and saving that ... they should keep their internal pointers on their own.
declare a list of nodes at the top of your file (and use pickle... just cause Im more familiar with that)
import pickle
kQuestion = 'question'
kGuess = 'guess'
questfile = 'questfile.txt'
nodes = []
....
change your load method to something like
def questor_file():
global nodes
try:
questor = open(questfile,'rb')
try:
nodes= pickle.load(questor)
quest = nodes[0]
questor.close()
return quest
except:
print 'P.load failed'
nodes = []
except:
print 'File did not open'
nodes = []
return Qnode('python')
change your class constructor so that it adds each node to nodes
class Qnode:
# initialization method
def __init__(self,guess):
self.nodetype = kGuess
self.desc = guess
nodes.append(self)
at the end where it says #added save dictionary , save your list of nodes
questor = open(questfile,'wb')
q = pickle.dump(nodes,questor)
make sure you exit the program by typing no when prompted ...
you could also save it to a database or whatever but you would still have to store each node and it might be more complicated... this method should really be fine I think , (although there may be a more natural way to save a tree structure) ...
When I run this code in the command prompt, the Person I create is automatically deleted, but when in IDLE the deletion does not occur. Why?
NOTE: This is a program that is supposed to create an address book (a list of dictionaries)
Here is my code:
list = []
class bookEntry(dict):
total = 0
def __init__(self):
bookEntry.total += 1
self.d = {}
def __del__(self):
bookEntry.total -= 1
list.remove(self)
class Person(bookEntry):
def __init__(self, n):
bookEntry.__init__(self)
self.n = n
print '%s has been created' % (self.n)
def __del__(self):
print '%s has been deleted' % (self.n)
def addnewperson(self, n, e = '', ph = '', note = ''):
self.d['name'] = n
self.d['email'] = e
self.d['phone'] = ph
self.d['note'] = note
list.append(self)
I run the code with a startup function:
def startup():
aor = raw_input('Hello! Would you like to add an entry or retrieve one?')
if aor == 'add':
info = raw_input('Would you like to add a person or a company?')
if info == 'person':
n = raw_input('Please enter this persons name:')
e = raw_input('Please enter this persons email address:')
ph = raw_input('Please enter this persons phone number:')
note = raw_input('Please add any notes if applicable:')
X = Person(n)
X.addnewperson(n, e, ph, note)
startup()
When the code is run in IDLE I receive the following prompts, and submit the following answers:
'''
Hello! Would you like to add an entry or retrieve one?add
Would you like to add a person or a company?person
Please enter this persons name:Pig
Please enter this persons email address:pig#brickhouse.com
Please enter this persons phone number:333-333-3333
Please add any notes if applicable:one of three
Pig has been created
'''
Here, Pig is created and is not deleted. But in cmd.....
'''
Hello! Would you like to add an entry or retrieve one?add
Would you like to add a person or a company?person
Please enter this persons name:Pig
Please enter this persons email address:pig#brickhouse.com
Please enter this persons phone number:333-333-3333
Please add any notes if applicable:one of three
Pig has been created
Pig has been deleted
'''
Why is Pig being deleted?? __del__ is never called...
While you run in IDLE, the python process is still running even after you execute this program unless you exit from IDLE. But in command line, the python process executes your program and exits itself. So, that is where __del__ comes into play. When the reference count of the object is zero, it is automatically called to destroy it. so your object is deleted. When your program is ended and python process is itself terminated, there is no need for it to exist as well.
Reference
When the program ends, all variables are automatically deleted (otherwise, there would be a memory leak). IDLE keeps the environment open so you can keep using the variables you've created.
Note: My original answer was mistaken- I missed the lines
list = []
and
list.append(self)
I am writing a program to add to and update an address book. Here is my code:
EDITED
import sys
import os
list = []
class bookEntry(dict):
total = 0
def __init__(self):
bookEntry.total += 1
self.d = {}
def __del__(self):
bookEntry.total -= 1
list.remove(self)
class Person(bookEntry):
def __init__(self, n):
self.n = n
print '%s has been created' % (self.n)
def __del__(self):
print '%s has been deleted' % (self.n)
def addnewperson(self, n, e = '', ph = '', note = ''):
self.d['name'] = n
self.d['email'] = e
self.d['phone'] = ph
self.d['note'] = note
list.append()
def updateperson(self):
key = raw_input('What else would you like to add to this person?')
val = raw_input('Please add a value for %s' % (key))
self.d[key] = val
def startup():
aor = raw_input('Hello! Would you like to add an entry or retrieve one?')
if aor == 'add':
info = raw_input('Would you like to add a person or a company?')
if info == 'person':
n = raw_input('Please enter this persons name:')
e = raw_input('Please enter this persons email address:')
ph = raw_input('Please enter this persons phone number:')
note = raw_input('Please add any notes if applicable:')
X = Person(n)
X.addnewperson(n, e, ph, note)
startup()
When I run this code I get the following error:
in addnewperson
self.d['name'] = n
AttributeError: 'Person' object has no attribute 'd'
I have two questions:
UPDATED QUESTIONS
1. why isnt the d object being inherited from bookentry()?
I know this question/code is lengthy but I do not know where to go from here. Any help would be greatly appreciated.
The addnewperson shoud have 'self' as first argument; actually, the name doesn't matter ('self' is just a convention), but the first argument represent the object itself. In your case, it's interpreting n as the "self" and the other 3 as regular arguments.
____del____ must not take arguments besides 'self'.
Edit: BTW I spotted a few other problems in your example, that maybe you're not aware of:
1) d in bookentry is a class member, not an instance member. It's shared by all bookentry's instances. To create an instance member, use:
class bookentry(dict):
def __init__(self,n):
self.d = {}
# rest of your constructor
2) you're trying to access d directly (as you would do in Java, C++ etc), but Python doesn't support that. You must have a 'self' parameter in your methods, and access instance variables through it:
class person(bookentry):
def foo(self,bar):
self.d[bar] = ...
person().foo(bar)
Update: for the last problem, the solution is to call the super constructor (which must be done explicitly in Python):
class Person(bookEntry):
def __init__(self, n):
super(Person, self).__init__()
self.n = n
print '%s has been created' % (self.n)
A brief explanation: for people with background in OO languages without multiple inheritance, it feels natural to expect the super type constructor to be called implicitly, automatically choosing the most suitable one if no one is mentioned explicitly. However, things get messy when a class can inherit from two or more at the same time, for this reason Python requires the programmer to make the choices himself: which superclass constructor to call first? Or at all?
The behavior of constructors (and destructors) can vary wildly from language to language. If you have further questions about the life cycle of Python objects, a good place to start would be here, here and here.
why isnt the d object being inherited from bookentry()?
That's because __init__ of the bookEntry is not called in the __init__ of the Person:
super(Person, self).__init__()
BTW, why inherit from dict if its functionality is not used? It's better to remove it and inherit from object instead (also class names are usually CamelCased):
class BookEntry(object):