Related
EDIT: CHECK AT THE BOTTOM FOR A MORE CLEAR VIEW OF WHAT I AM DOING, PLEASE!
As an example, let's say I have information on three cars:
Car One
500hp
180mph
15mpg
Car Two
380hp
140mph
24mpg
Car Three
450hp
170mph
20mpg
I want to put that in a dictionary, or SOMETHING, so that I can easily access it through a function.
def fuel_eco(car):
return("The fuel economy for %s is %s" % (car, mpg))
def top_speed(car):
return("The top speed for %s is %s" % (car, speed))
def horsepower(car):
return("The horsepower for %s is %s" % (car, hp))
Basically have a module with some functions and a list/dictionary/whatever of the information, and then have another script that asks what car they want to view info on, and what information they want to know.
import carstats
car = input("What car do you want to find out about?")
stat = input("What information do you want to know?")
getStat = getattr (carstats, stat)
print(getStat(car))
How do I store the information for the three vehicles (And more if I add them) in a dictionary, so I can retrieve the information?
Okay, these are the actual files I am working with:
File one is asoiaf.py:
def sigil (house):
"""
Function to return a description of the sigil of a specified Great House.
Takes one argument, the name of the House.
"""
house = house.lower ()
if house == "stark":
sigil = "a grey direwolf on a white field."
elif house == "lannister":
sigil = "a golden lion rampant on a crimson field."
elif house == "targaryen":
sigil = "a red three-headed dragon on a black field."
else:
sigil = "Unknown"
house = str(house[0].upper()) + str(house[1:len(house)])
return("The sigil for House %s is %s" % (house, sigil))
def motto (house):
"""
Function to return the family motto of a specified Great House.
Takes one argument, the name of the House.
"""
house = house.lower ()
if house == "stark":
motto = "Winter is coming!"
elif house == "lannister":
motto = "Hear me roar!"
elif house == "targaryen":
motto = "Fire and blood!"
else:
motto = "Unknown"
house = str(house[0].upper()) + str(house[1:len(house)])
return("The motto for House %s is: %s" % (house, motto))
The second file is encyclopedia.py:
import asoiaf
#import sl4a
#droid = sl4a.Android ()
#sound = input ("Would you like to turn on sound?")
info = "yes"
while info == "yes":
#if sound == "yes":
# droid.ttsSpeak ("What house do you want to learn about?")
house = input ("What house do you want to learn about?")
house = str(house[0].upper()) + str(house[1:len(house)])
#if sound == "yes":
# droid.ttsSpeak ("What do you want to know about House %s?" % house)
area = input ("What do you want to know about House %s?" % house)
getArea = getattr (asoiaf, area)
#if sound == "yes":
# droid.ttsSpeak (getArea (house))
print (getArea (house))
#if sound == "yes":
# droid.ttsSpeak ("Would you like to continue learning?")
info = input ("Would you like to continue learning?")
if info == "no":
print ("Goodbye!")
You'll see a lot of commenting out in the last code, because I had to comment out the TTS that I have for my phone, since most people are not on an Android right now. As you can see, I am using IF, ELIF, ELSE in the functions, and I am just trying to see if there is an easier way. I apologize if it is/was confusing.
Creating a class should be the best way to do it:
class Car: # define the class
def __init__(self, name, speed, hp, mpg):
# This is the constructor. The self parameter is handled by python,
# You have to put it. it represents the object itself
self.name = name
self.speed = speed
self.hp = hp
self.mpg = hp
# This bind the parameters to the object
# So you can access them throught the object
You can then use the object this way:
my_car1 = Car('Car One', 180, 500, 15)
my_car1.speed # Will return 180
Concercing the __init__ name, it has to be this name, all constructors have this name (that's how Python know it is the class constructor). The __init__ method is called when you call Car('car one', 180, 500, 15). You have to ommit the self parameter, Python handle it.
You can add other function to your class, like
def top_speed(self):
return 'The top speed is {}'.format(self.speed)
Then you simply have to do my_car1.topspeed()
In every function you define in a class self must be the first parameter (except some rare cases such as classmethod or staticmethods). Obviously the topseed function works only if you create it in the class Car: block.
I'd suggest you should read more about object oriented programming (OOP) in Python. Just google OOP python and you will have a lot of serious ressources explaining you how to create classes and how to use them.
This official python classes tutorial should help you a lot in understanding the concept.
EDIT:
Regarding the accessing of the class in an other script. It's simple:
let's say you save the code above in a car.py file. Just place that file in the same folder as your other script, and in your other script do:
from car import Car # car is the name of the .py file, Car is the class you want to import
name = input('Car name: ')
speed = int(input('Car speed: ')) # input return a string, you have to cast to an integer to have a number
hp = int(input('Car hp: '))
mpg = int(input('Car mpg : '))
my_car = Car(name,speed,hp,mpg) # Then you just create a Car Object with the data you fetched from a user.
stuff = my_car.speed * my_car.hp # An example of how to use your class
print('The given car have {} mph top speed and have {} horsepower'.format(my_car.speed,my_car.hp))
What you have to understand is that a Class is some kind of a formated data type. When creating a Car class, you are defining how to create a car object. And Each time you call Car(...), you actually create one of these object, the value you put in the object are whatever values you want to put. It could be random number, user input or even network fetched data. You can use this object as you want.
Edit 2:
Given your code. Creating classes will change some things. Let's Give an example.
File 1 houses.py:
class House: # defining a house class
def __init__(self,name, sigil, motto):
self.name = name
self.sigil = sigil
self.moto = motto
# Then, in the same file, you create your houses.
starks = House('starks','grey direwolf on a white field','Winter is coming!')
lannisters = House('lannisters', 'a golden lion rampant on a crimson field', 'Hear me roar!')
# let's skip targaryen, it's the same way...
unknown_house = House('unknown','unknown','unknow')
houses = [starks, lannisters]
def get_house(name):
for house in houses:
if house.name == name:
return house
return unknow_house # if no house match, return unknow
Then in your second file. You just se that:
import houses
house_wanted = input('What house do you want to know about?')
my_house = houses.get_house(house_wanted)
print('this is the house {}; Sigil {} and motto {}'.format(my_house.name, my_house.sigil, my_house.motto))
If you plan on working on biggers set. You should have a look at Enums. That could fit what you want.
If you want to getting a precise attribute, you can do it this way:
import houses
house_wanted = input('What house do you want to know about?')
my_house = houses.get_house(house_wanted)
attr= input('What do you want to know about that house?')
print(getattr(my_house,attr.lower()))
Note this last thing will raise an error if you call for non-existent attr (like foo).
There are many ways to solve the broader problem you describe in the text of your question (the question of how to store multiple pieces of information about an object). Classes maybe one good one. Classes have the advantage of better robustness than dictionaries.
To answer the specific question in the summary/title: "how to have more than one item associated with one key in a dictionary" - use dictionaries as the values, like this:
car_info = {'CarOne': {'power': 500, 'speed': 180, 'mileage': 18},
'CarTwo': {'power': 380, 'spead': 200, 'mileage': 10}
}
print "Car Two has power %d mileage %d" % (car_info['CarTwo']['power'], car_info['CarTwo']['mileage'])
You can see that this is not especially robust by trying to access the 'speed' for "CarTwo". If you look closely you will see that because I made a deliberate typo in the initializer for CarTwo, it does not have a speed at all, it has a spead. Classes will catch this error, dictionaries will not.
This is not a reason not to do it with dictionaries - just something to be aware of when deciding for your particular case.
You could create a class, called car, with whatever attributes you want!
Here's a great tutorial on how to do that: class tutorial
I'm on the road right now, but if you're having trouble, please tell me so that I can write some useful code...
I am basically trying to extract song names from a playlist, and then I want to create a class for each song in the playlist that includes all the relevant data for that song (song title, artist, location of each song etc.)
I have a list where each item in the list is a song name. I am trying to create a loop that creates a new class named after each item in the list.
This is what I have so far:
class MyClass():
var = "hi"
songNames = ['song1', 'song2', 'song3']
for name in songNames:
name = MyClass()
This is not working and I think it's because Python is having trouble/can't assign a name to a class like that. I am a beginner with Python and after extensive searching I could not come up with a solution. What is the correct way to do this?
So I've been working on the code and have made some progress:
class SongData(object):
def __init__(self, title="", artist="", directory=""):
self.title, self.artist, self.directory = title, artist, directory
def __str__(self):
desc_str = "Title: %s \nArtist: %s \nDirectory: %s \n" %(self.title,
self.artist, self.directory)
print desc_str
songNames = ['song1', 'song2', 'song3']
artistNames = ['artist1', 'artist2', 'artist3']
dirNames = ['dir1', 'dir2', 'dir3']
songs = {name: SongData(title=name) for name in songNames}
artists = {band: SongData(artist=band) for band in artistNames}
directorys = {direc: SongData(directory=direc) for direc in dirNames}
I would like to be able to print out the desc_str for each song so it appears like this:
Title: song1
Artist: artist1
Directory: dir1
But so far I have only managed to call one data category at once, so it prints Title: song1 but leaves the Artist: and Directory: sections blank.
How can I get it to print them out all at once?
You don't want to create a class for each song; you want to create an instance of a class for each song. For example:
class Song(object):
def __init__(self, name):
self.name = name
songNames = ['song1', 'song2', 'song3']
songs = []
for name in songNames:
songs.append(Song(name))
Notice that I'm storing the Song objects in a list. You need to store them somewhere, or you can't access them again later, right?
If you want to be able to access them by name later, instead of just looping over all of them:
songs = {}
for name in songNames:
songs[name] = Song(name)
And you can convert either of those into a one-liner using a comprehension:
songs = [Song(name) for name in songNames]
songs = {name: Song(name) for name in songNames}
From a comment:
I had intended by class to look something like the following, because every song was going to have all the same variables:
class songData:
title = ""
artist = ""
directory = ""
tempStr = ""
def description(self):
desc_str = "%s is by %s, located at %s" %(self.title, self.artist,self.directory)
return desc_str
This is wrong for the same reasons I already explained: You're creating class attributes, not instance attributes. This means every instance will share a single title, etc., which is not what you want. Also, it means self.title is misleading (although it will work).
Meanwhile, if this is Python 2, you're again creating a classic class. Also, you probably want a __str__ method rather than a special description method, because then you can just print(song) instead of having to print(song.description()). Finally, it's a bit confusing to use the same naming convention for variables and classes.
What you probably want is:
class SongData:
def __init__(self, title="", artist="", directory=""):
self.title, self.artist, self.directory = title, artist, directory
def __str__(self):
desc_str = "%s is by %s, located at %s" % (self.title, self.artist,self.directory)
return desc_str
Now, you can use this basically the same way as the Song class above:
songNames = ['song1', 'song2', 'song3']
songs = {name: SongData(title=name) for name in songNames}
Now you can do things like this:
name = input('What song do you want info on?') # raw_input, if Python 2
print(songs[name])
(Of course this isn't very useful unless you also have code that sets the artist and directory somewhere, because it'll just print Song Windowlicker is by , located at. But I don't know where you intend to get those from.)
Your new code has two problems. First:
def __str__(self):
desc_str = "Title: %s \nArtist: %s \nDirectory: %s \n" %(self.title,
self.artist, self.directory)
print desc_str
You need to return desc_str here, not print it.
Second:
songNames = ['song1', 'song2', 'song3']
artistNames = ['artist1', 'artist2', 'artist3']
dirNames = ['dir1', 'dir2', 'dir3']
songs = {name: SongData(title=name) for name in songNames}
artists = {band: SongData(artist=band) for band in artistNames}
directorys = {direc: SongData(directory=direc) for direc in dirNames}
Here you're creating three separate collections of SongData objects, each of which only has one attribute filled.
The key here is zip. It's one of the most useful functions in Python once you get what it does, but until you know about it you'd never think to look for it. It's easier to describe if I show what it does:
>>> zip(songNames, artistNames, dirNames)
[('song1', 'artist1', 'dir1'),
('song2', 'artist2', 'dir2'),
('song3', 'artist3', 'dir3')]
So, that gives you a list of tuples, where each tuple has a song name, an artist, and a dir. The first tuple is the first of each, the second is the second of each, etc.
Now you can build a SongData out of each tuple pretty easily:
songs = {}
for title, artist, directory in zip(songNames, artistNames, dirNames):
songs[title] = SongData(title, artist, directory)
As a dict comprehension, it gets a little verbose:
songs = {title: SongData(title, artist, directory)
for title, artist, directory in zip(songNames, artistNames, dirNames)}
But you can simplify it with another trick: unpacking arguments:
songs = {t[0]: SongData(*t) for songtuple in zip(songNames, artistNames, dirNames)}
Of course you could do this without zip, but it would look like a mess:
songs = {SongData(songNames[i], artistNames[i], dirNames[i])
for i in range(len(songNames))}
… and if you have a small bug with mismatched lists, it will be hard to understand and debug this way. Generally, whenever you write for i in range(len(foo)) in Python, there's probably a simple way.
However you build it, you can use it just as you'd expect:
>>> print songs['song1']
Title: song1
Artist: artist1
Directory: directory1
While we're at it, you probably don't want a space at the end of each line of output. It's not visible to humans, but it could confuse code you later write to parse the output, and it wastes space, and there's no real benefit. Just put each \n right after the %s.
I agree that it seems like you want to create an instance rather than a class for each song. But if you do want to create a class for each song, use the type(className, parentTuple, attributeDict) function. It returns a new class with the given name.
E.g.
songClasses = []
for name in songNames:
songClasses.append(type(name, (), {}))
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.
I am having problems understanding how to work with query results. I asked about half a dozen questions about this but I still do not understand. I copy from previous code and I make it work somehow but since I don't understand the underlying concept the code breaks down if I make a minor change. I would really appreciate if you could tell me how you visualize what is happenning here and explain it to me. Thank you.
class ReceiveEmail(InboundMailHandler):
def receive(self, message):
logging.info("Received email from %s" % message.sender)
plaintext = message.bodies(content_type='text/plain')
for text in plaintext:
txtmsg = ""
txtmsg = text[1].decode()
logging.info("Body is %s" % txtmsg)
logging.info("CC email is %s" % ((message.cc).split(",")[1]))
query = User.all()
query.filter("userEmail =", ((message.cc).split(",")[1]))
results = query.fetch(1)
for result in results:
result.userScore += 1
um = results[0]
um.userScore = result.userScore
um.put()
In this code, as I understand it, the query takes the second email address from the cc list and fetches the result.
Then I increment the userScore by 1.
Next, I want to update this item in Datastore so I say
um = results[0]
um.userScore = result.userScore
um.put()
But this gives an index out of range error:
um = results[0]
IndexError: list index out of range
Why? I am imagining that results[0] is the zeroeth item of the results. Why is it out of range? Only thing I can think of is that, the list may be None. But I don't understand why. It must have the 1 item that was fetched.
Also, if I try to test for the first email address by changing the index from [1] to [0]
query.filter("userEmail =", ((message.cc).split(",")[0]))
then I don't get the IndexError.
What am I doing wrong here?
Thanks!
EDIT
See comments:
(message.cc).split(",")[0])
left a space in front of the emails (starting with the second email), so the query was not matching them;
>>> cc.split(",")
['cc12#example.com', ' cc13#example.com', ' cc13#example.com']
adding a space after comma fixed the problem:
>>> listcc = cc.split(", ")
>>> listcc
['cc12#example.com', 'cc13#example.com', 'cc13#example.com']
>>>
To understand the code break it down and look at it piece by piece:
class ReceiveEmail(InboundMailHandler):
def receive(self, message):
logging.info("Received email from %s" % message.sender)
# Get a list of CC addresses. This is basically a for loop.
cc_addresses = [address.strip() for address in message.cc.split(",")]
# The CC list goes with the message, not the bodies.
logging.info("CC email is %s" % (cc_addresses))
# Get and iterate over all of the *plain-text* bodies in the email.
plaintext = message.bodies(content_type='text/plain')
for text in plaintext:
txtmsg = ""
txtmsg = text[1].decode()
logging.info("Body is %s" % txtmsg)
# Setup a query object.
query = User.all()
# Filter the user objects to get only the emails in the CC list.
query.filter("userEmail IN", cc_addresses)
# But, only get at most 10 users.
users = query.fetch(10)
logging.info('Got %d user entities from the datastore.' % len(users))
# Iterate over each of the users increasing their score by one.
for user in users:
user.userScore += 1
# Now, write the users back to the datastore.
db.put(users)
logging.info('Wrote %d user entities.' % len(users))
I would make an adjustment to your model structure. When you create the User entity, I would set the key_name to the email address. You will be able to make your queries much more efficient.
Some references:
List Comprehension.
Query Object.
db.put().
I have a combo, which is showing some awkward behavior. Given a list of options from the combo-box, the user should pick the name of a city clicking with the mouse. Here is the code:
QtCore.QObject.connect(self.comboCity, QtCore.SIGNAL("currentIndexChanged(QString)"), self.checkChosenCity)
def checkChosenCity(self):
self.cityName=self.comboCity.currentText()
print "the city chosen is:"
print "%s" % self.cityName
The problem is, each time a city is chosen, connect calls the function checkChosenCity twice.
This combo is a hierarchical combo, i.e. after in the first combo a customer is chosen, then in the second combo-box comes the list of cities for that customer.
I hope someone here can point out or guess why this is happening.
I had exactly the same problem. After some debugging it turned out that using
currentIndexChanged(int)
instead of
currentIndexChanged(QString)
fixed it for me.
It still don't understand why the former fires twice.
Thanks Eli..
Here is what I have:
combo1 : [customernames] - pick a customer.
combo2 : [cityList] - pick a city for the chosen customer.
combo3 : [emploeeList] - load employees for that city, given the chosen customer.
What I find out is that, even when no city is chosen, the combox-box for city is activated. And yes, I have checked if the function 'checkChosenCity' is not called anywhere else inside the program.
As a quick fix, not the ideal solution, I put a condition to avoid the problem into the function 'checkChosenCity'. So, now when this function is wrongly activated by 'connect', it checks if a city name was really selected, if none, then the pointed process doesn't run avoiding the system to crash.
Here is the function that loads the city list into combo-box:
def loadcomboCity(self,customerName):
if customerName == " ":
"""no customer was chosen yet - list of city empty"""
id=0
CityName=" "
self.addcomboCity(id,CityName)
else:
"""for this customerName - load his city list"""
self.loadCityList_mysql(customerName)
lin=0
""" the data is imported from mysql class into self.db.matrix"""
for row in self.db.matrix:
id=lin
cityname=self.db.matrix[lin][0]
print "city name = %s" % cityname
self.addcomboCity(id,cityname)
lin=lin+1
Here is the function that loads the customer-names list into combo-box:
def loadComboCustomer(self):
"""queries customerList into self.connexDB.matrix"""
self.loadCustomerList_mysql()
lin=0
""" the data is imported from mysql class into self.db.matrix"""
for row in self.connexDB.matrix:
id=lin
customername=self.connexDB.matrix[lin][0]
self.addcomboCustomer(id,customername)
lin=lin+1
Here is the function that checkes if the a customer name was chosen:
def checkChosenCustomer(self):
self.customerName=self.comboCustomer.currentText()
print "the customer chosen is:"
print "%s" % self.customerName
self.loadcomboCity(self.customerName)
Here is the function that checks if some city chosen from list into combo-box:
def checkChosenCity(self):
self.CityName=self.comboCity.currentText()
print "the City chosen is:"
print "value of City = %s" % self.CityName
if self.CityName == '':
print "empty"
else:
"""for this city - load the respective customer employee list"""
self.buildListOfEmployees_mysql(self.CityName)
""" the data is imported from mysql class into self.db.matrix"""
for row in self.db.matrix:
id=lin+1
personname=self.db.matrix[lin][0]
print "person name = %s" % personname
self.addcomboPerson(id,personname)
lin=lin+1
Here is the main function that connect combo-box events:
def options(self):
self.comboCustomer = QtGui.QComboBox(self.boxBooking)
self.comboCustomer.setGeometry(QtCore.QRect(60, 60, 521, 22))
self.loadComboCustomer()
QtCore.QObject.connect(self.comboCustomer, QtCore.SIGNAL("currentIndexChanged(QString)"), self.checkChosenCustomer)
self.comboCity = QtGui.QComboBox(self.boxBooking)
self.comboCity.setGeometry(QtCore.QRect(60, 120, 521, 22))
self.loadcomboCity(self.customerName)
QtCore.QObject.connect(self.comboCity, QtCore.SIGNAL("currentIndexChanged(QString)"), self.checkChosenCity)
Not the ideal solution really. But, quite funny to have to spend hours to find out that such a strange connect event is being wrongly self-activated.
If you discover any other explanation, just let us know.