tags:

views:

1939

answers:

9

I have a method in my Python code that returns a tuple - a row from a SQL query. Let's say it has three fields: (jobId, label, username)

For ease of passing it around between functions, I've been passing the entire tuple as a variable called 'job'. Eventually, however, I want to get at the bits, so I've been using code like this: (jobId, label, username) = job

I've realised, however, that this is a maintenance nightmare, because now I can never add new fields to the result set without breaking all of my existing code. How should I have written this?

Here are my two best guesses: (jobId, label, username) = (job[0], job[1], job[2]) ...but that doesn't scale nicely when you have 15...20 fields

or to convert the results from the SQL query to a dictionary straight away and pass that around (I don't have control over the fact that it starts life as a tuple, that's fixed for me)

+12  A: 

I'd say that a dictionary is definitely the best way to do it. It's easily extensible, allows you to give each value a sensible name, and Python has a lot of built-in language features for using and manipulating dictionaries. If you need to add more fields later, all you need to change is the code that converts the tuple to a dictionary and the code that actually makes use of the new values.

For example:

job={}
job['jobid'], job['label'], job['username']=<querycode>
Chris Upchurch
see also http://code.activestate.com/recipes/81252/
Jay
A: 

With a tuple it will always be a hassle to add or change fields. You're right that a dictionary will be much better.

If you want something with slightly friendlier syntax you might want to take a look at the answers this question about a simple 'struct-like' object. That way you can pass around an object, say job, and access its fields even more easily than a tuple or dict:

job.jobId, job.username = jobId, username
dF
+2  A: 

Perhaps this is overkill for your case, but I would be tempted to create a "Job" class that takes the tuple as its constructor argument and has respective properties on it. I'd then pass instances of this class around instead.

Ryan Duffield
+2  A: 

I would use a dictionary. You can convert the tuple to a dictionary this way:

values = <querycode>
keys = ["jobid", "label", "username"]
job = dict([[keys[i], values [i]] for i in xrange(len(values ))])

This will first create an array [["jobid", val1], ["label", val2], ["username", val3]] and then convert that to a dictionary. If the result order or count changes, you just need to change the list of keys to match the new result.

PS still fresh on Python myself, so there might be better ways off doing this.

Staale
See Aaron Maenpaa answer of Zip. A common newbie mistake in Python is using list comprehension when zip will suffice.
Daniel Goldberg
A: 

How about this:

class TypedTuple:
    def __init__(self, fieldlist, items):
       self.fieldlist = fieldlist
       self.items = items
    def __getattr__(self, field):
       return self.items[self.fieldlist.index(field)]

You could then do:

j = TypedTuple(["jobid", "label", "username"], job)
print j.jobid

It should be easy to swap self.fieldlist.index(field) with a dictionary lookup later on... just edit your __init__ method! Something like Staale does.

Daren Thomas
+9  A: 

@Staale

There is a better way:

job = dict(zip(keys, values))
Aaron Maenpaa
This is also very maintainable code, the only "problem" is rebuilding the code to deal with dictionaries.
Daniel Goldberg
+1  A: 

An old question, but since no one mentioned it I'll add this from the Python Cookbook:

Recipe 81252: Using dtuple for Flexible Query Result Access

This recipe is specifically designed for dealing with database results, and the dtuple solution allows you to access the results by name OR index number. This avoids having to access everything by subscript which is very difficult to maintain, as noted in your question.

Jay
+1  A: 

This is an old question, but...

I'd suggest using a named tuple in this situation: collections.namedtuple

This is the part, in particular, that you'd find useful:

Subclassing is not useful for adding new, stored fields. Instead, simply create a new named tuple type from the _fields attribute.

JAB
A: 

If you're using the MySQLdb package, you can set up your cursor objects to return dicts instead of tuples.

import MySQLdb, MySQLdb.cursors
conn = MySQLdb.connect(..., cursorclass=MySQLdb.cursors.DictCursor)
cur = conn.cursor() # a DictCursor
cur2 = conn.cursor(cursorclass=MySQLdb.cursors.Cursor) # a "normal" tuple cursor
jpkotta