Newbie Question: Obtain element from list of tuples

H

HoneyMonster

Hi,

I'm just starting to learn Python, so please bear with me. I have in my
program an object (recs) which is a list of tuples (returned as such by a
database query).

My question is doubtless a very easy one to answer: Say I want the ninth
element in the twentieth tuple put into variable PID, I can do this,
bearing in mind that numbering starts at zero:

tup = recs[19]
PID = tup[8]

But there must be an easier way; i.e. to do it in one go without the
extra variable. How do I achieve that please?

Secondly, I am more than happy to read the fine manuals. If someone could
point me in the direction of one or two "for idiots" guides, that would
be greatly appreciated.
 
A

Arnaud Delobelle

Hi,

I'm just starting to learn Python, so please bear with me. I have in my
program an object (recs) which is a list of tuples (returned as such by a
database query).

My question is doubtless a very easy one to answer: Say I want the ninth
element in the twentieth tuple put into variable PID, I can do this,
bearing in mind that numbering starts at zero:

tup = recs[19]
PID = tup[8]

But there must be an easier way; i.e. to do it in one go without the
extra variable. How do I achieve that please?

Well, you were almost there:

pid = recs[19][8]

Note: all caps is usually reserved for constants in Python.
 
R

Roy Smith

HoneyMonster said:
Hi,

I'm just starting to learn Python, so please bear with me. I have in my
program an object (recs) which is a list of tuples (returned as such by a
database query).

Sounds like a standard database interface -- each tuple represents one
row in the query result. Very common.
My question is doubtless a very easy one to answer: Say I want the ninth
element in the twentieth tuple put into variable PID, I can do this,
bearing in mind that numbering starts at zero:

tup = recs[19]
PID = tup[8]

Sure, you could do:

PID = recs[19][8]

That's shorter, but it probably not better. Presumably, the tuple
elements represent columns in the database. It would make the code
easier to read if you unpacked the tuple into something with
human-readable names:

col_1, col_2, col_3, ...... col_n = recs[19]

and then use the name for the column of interest. A common convention
is that when you're unpacking a tuple (or a list, I suppose) and are
only interested in some of the elements, you unpack the others into "_".
Thus:

_, _, _, _, pid, _, _, _ = recs[19]

Of course, if you're going to do all that, you probably could have
written a better query which returned just the column you were
interested in. Instead of "select * from foo", you could have done
"select pid from foo" (assuming you're using some SQL-based database).
Secondly, I am more than happy to read the fine manuals. If someone could
point me in the direction of one or two "for idiots" guides, that would
be greatly appreciated.

The on-line tutorial is pretty good.
http://docs.python.org/tutorial/index.html
 
H

HoneyMonster

HoneyMonster said:
Hi,

I'm just starting to learn Python, so please bear with me. I have in my
program an object (recs) which is a list of tuples (returned as such by
a database query).

Sounds like a standard database interface -- each tuple represents one
row in the query result. Very common.
My question is doubtless a very easy one to answer: Say I want the
ninth element in the twentieth tuple put into variable PID, I can do
this, bearing in mind that numbering starts at zero:

tup = recs[19]
PID = tup[8]

Sure, you could do:

PID = recs[19][8]

That's shorter, but it probably not better. Presumably, the tuple
elements represent columns in the database. It would make the code
easier to read if you unpacked the tuple into something with
human-readable names:

col_1, col_2, col_3, ...... col_n = recs[19]

and then use the name for the column of interest. A common convention
is that when you're unpacking a tuple (or a list, I suppose) and are
only interested in some of the elements, you unpack the others into "_".
Thus:

_, _, _, _, pid, _, _, _ = recs[19]

Of course, if you're going to do all that, you probably could have
written a better query which returned just the column you were
interested in. Instead of "select * from foo", you could have done
"select pid from foo" (assuming you're using some SQL-based database).
Secondly, I am more than happy to read the fine manuals. If someone
could point me in the direction of one or two "for idiots" guides, that
would be greatly appreciated.

The on-line tutorial is pretty good.
http://docs.python.org/tutorial/index.html

Thanks, Arnaud and Roy. I had no idea it could be so easy. I think I
rather like Python!

OK, I'll change PID to pid if that's best practice.

Yes, it's an SQL-based database, PostgreSQL actually. I'm using psycopg2
to access it and wxGlade to construct the GUI interface. It all seems to
be progressing very smoothly.

Thanks again.
 
C

Chris Angelico

My question is doubtless a very easy one to answer: Say I want the ninth
element in the twentieth tuple put into variable PID, I can do this,
bearing in mind that numbering starts at zero:

tup = recs[19]
PID = tup[8]

But there must be an easier way; i.e. to do it in one go without the
extra variable. How do I achieve that please?

The specific answer has already been given, but I'd like to fill in
the generality. In Python, everything's an object; if you can do
something with a variable, you can do it with an expression too. I
don't know what your function call is to obtain your record list, but
imagine it's:

recs = conn.query("SELECT * FROM some_table")

Then you can actually do all of this in a single statement. It's not
usually what you'll want to do, but this is legal:

pid = conn.query("SELECT * FROM some_table")[19][8]

If you're absolutely certain that you'll always get precisely one
value from a query, this becomes rather more useful:

mode = conn.query("SELECT mode FROM config WHERE id=5")[0][0]

In any case: You can work with a function's return value directly,
without first storing it in a temporary variable.

Hope that helps!

Chris Angelico
 
R

Roy Smith

Chris Angelico said:
If you're absolutely certain that you'll always get precisely one
value from a query, this becomes rather more useful:

mode = conn.query("SELECT mode FROM config WHERE id=5")[0][0]

Although, if you're going to do that, you might as well take advantage
of the fetchone() method that most Python database APIs have and
eliminate one of the indexes. Typical code would be something like:

cursor.execute("SELECT mode FROM config WHERE id=5")
mode = cursor.fetchone()[0]

If you're going to be doing any database work in Python, you should be
familiar with the Python Database API Specification,
http://www.python.org/dev/peps/pep-0249/. Most of the vendor-specific
database modules have interfaces which hold pretty close to the style
described there.

You might also want to explore SQL_Alchemy. I personally find it
difficult and painful to work with, but it does have the powerful
advantage that it abstracts away all the vendor-specific differences
between databases.
 
H

HoneyMonster

My question is doubtless a very easy one to answer: Say I want the
ninth element in the twentieth tuple put into variable PID, I can do
this, bearing in mind that numbering starts at zero:

tup = recs[19]
PID = tup[8]

But there must be an easier way; i.e. to do it in one go without the
extra variable. How do I achieve that please?

The specific answer has already been given, but I'd like to fill in the
generality. In Python, everything's an object; if you can do something
with a variable, you can do it with an expression too. I don't know what
your function call is to obtain your record list, but imagine it's:

recs = conn.query("SELECT * FROM some_table")

Then you can actually do all of this in a single statement. It's not
usually what you'll want to do, but this is legal:

pid = conn.query("SELECT * FROM some_table")[19][8]

If you're absolutely certain that you'll always get precisely one value
from a query, this becomes rather more useful:

mode = conn.query("SELECT mode FROM config WHERE id=5")[0][0]

In any case: You can work with a function's return value directly,
without first storing it in a temporary variable.

Hope that helps!

Thanks, Chris. Actually I had simplified the question somewhat. The
actuality is indeed - as you suspected - that recs = conn.query("SELECT *
FROM some_table") but I am then using the recs object to populate a (non
editable) wxGrid.

When the user selects a row and clicks a button, I am using:
pos = self.grid_1.GetGridCursorRow() to establish which tuple in recs is
involved, and then pid = recs[pos][4] to determine the key value (I
suspected that accessing recs directly would be more efficient that
trying to determine the wxGrid column value, and in any case the pid is
in recs but not in the wxGrid).

I trust this all makes sense. Thanks again.
 
C

Chris Angelico

When the user selects a row and clicks a button, I am using:
pos = self.grid_1.GetGridCursorRow() to establish which tuple in recs is
involved, and then pid = recs[pos][4] to determine the key value (I
suspected that accessing recs directly would be more efficient that
trying to determine the wxGrid column value, and in any case the pid is
in recs but not in the wxGrid).

Yep, this looks like a sensible way to do it!

I would recommend, though, that you avoid "magic numbers" - the
process ID might happen to be in cell 4, but what if your column list
changes? This is especially problematic when you use "select * from
table", because changes outside your code can break things.

There's two solutions. One is to carefully match your SELECT statement
to a set of constants:

exec("SELECT FOO,BAR,QUUX,ASDF,PID,WHATEVER FROM table")
FOO,BAR,QUUX,ASDF,PID,WHATEVER,*_=range(20) # Cool trick to avoid
having to count the columns

But a better way is to let the database handle it. Check your
interface library to see if you can have it return dictionaries or
objects instead of tuples - then you could use:

pid = recs[pos]["pid"]
# or
pid = recs[pos]->pid

which avoids the need to count columns at all.

Other than that, though, yep - your code concept looks fine.

ChrisA
 
A

alex23

Roy Smith said:
A common convention
is that when you're unpacking a tuple (or a list, I suppose) and are
only interested in some of the elements, you unpack the others into "_".
Thus:

_, _, _, _, pid, _, _, _ = recs[19]

Pre-namedtuple, I used to like using named slices for this:

cPID = slice(19)
pid = recs[cPID]

cHostPort = slice(99,100)
host, port = recs[cHostPort]

etc.

The suggestions of having your query return a dictionary where
possible are the most ideal, but if it's not possible you can always
wrap the result tuple in a namedtuple:

from collections import namedtuple

Staff = namedtuple('Staff',
['firstname','lastname','age','position'])

sql_result = ('John', 'Example', '30', 'Dummy')
staff_record = Staff(sql_result)

print staff_record.firstname, staff_record.age
 
S

Steven D'Aprano

Pre-namedtuple, I used to like using named slices for this:

cPID = slice(19)
pid = recs[cPID]

You know, that is an incredibly simple thing and yet it never dawned on
me before now. Thank you for sharing that.
 
F

Frank Millman

Steven D'Aprano said:
Pre-namedtuple, I used to like using named slices for this:

cPID = slice(19)
pid = recs[cPID]

You know, that is an incredibly simple thing and yet it never dawned on
me before now. Thank you for sharing that.

I also like it, but it does not work quite so simply for me.
row = tuple(range(20))
cPID = slice(15)
pid = row[cPID]
pid (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)

This works -
row = tuple(range(20))
cPID = slice(15, 16)
pid, = row[cPID] # note the trailing comma
pid 15

Still nice, but not quite so elegant.

Am I missing something?

Frank Millman
 
D

DevPlayer

Pre-namedtuple, I used to like using named slices for this:
    cPID = slice(19)
    pid = recs[cPID]
You know, that is an incredibly simple thing and yet it never dawned on
me before now. Thank you for sharing that.

I also like it, but it does not work quite so simply for me.
row = tuple(range(20))
cPID = slice(15)
pid = row[cPID]
pid

(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)



This works -


row = tuple(range(20))
cPID = slice(15, 16)
pid, = row[cPID]  # note the trailing comma
pid
15

Still nice, but not quite so elegant.

Am I missing something?

Frank Millman

if you're going to do:
cPID = slice(15, 16)
pid, = row[cPID] # note the trailing comma

Why not just:
row = tuple(range(20))
cPID = 15 # a proposed constant
pid = row[cPID]
pid
15

It might be better for other's to debug if you use
as a little tiny comma like that is easy to miss in some text editors.
Not everyone has large LCD screens. Not that I like the "_" for this
purpose.

Personally I'd love to see the "_" used instead of "self" as a common
practice (useful when you use lots of "self...." on a line).
 
H

HoneyMonster

When the user selects a row and clicks a button, I am using:
pos = self.grid_1.GetGridCursorRow() to establish which tuple in recs
is involved, and then pid = recs[pos][4] to determine the key value (I
suspected that accessing recs directly would be more efficient that
trying to determine the wxGrid column value, and in any case the pid is
in recs but not in the wxGrid).

Yep, this looks like a sensible way to do it!

I would recommend, though, that you avoid "magic numbers" - the process
ID might happen to be in cell 4, but what if your column list changes?
This is especially problematic when you use "select * from table",
because changes outside your code can break things.

There's two solutions. One is to carefully match your SELECT statement
to a set of constants:

exec("SELECT FOO,BAR,QUUX,ASDF,PID,WHATEVER FROM table")
FOO,BAR,QUUX,ASDF,PID,WHATEVER,*_=range(20) # Cool trick to avoid having
to count the columns

But a better way is to let the database handle it. Check your interface
library to see if you can have it return dictionaries or objects instead
of tuples - then you could use:

pid = recs[pos]["pid"]
# or pid = recs[pos]->pid

which avoids the need to count columns at all.

Other than that, though, yep - your code concept looks fine.

Thanks again, Chris. I'm completely new to Python, but am an old had at
databases. I *never* use "select *" in a production environment; rather
an explicit list of columns. Thus it doesn't matter if a column is added
to the table, nor if the column order in the table is changed.

But I have used this at the start if my code to make it more readable
anyway:

# Define constants (position in tuple)
Title = 0
Episode = 1
Categories = 2
Channel = 3
PID = 4
Index = 5

so that in the bit where I populate the grid, the code is now:

cnt = 0
for tup in recs:
self.grid_1.SetCellValue(cnt, Title, tup[Title])
self.grid_1.SetCellValue(cnt, Episode, tup[Episode])
self.grid_1.SetCellValue(cnt, Categories, tup[Categories])
self.grid_1.SetCellValue(cnt, Channel, tup[Channel])
cnt = cnt + 1

just so that is more legible.

Cheers,
HM
 
A

alex23

Am I missing something?

No, I seem to be. I have _no_ idea how I got that original syntax to
work :|

My apologies, DevPlayer's suggestion is much more sensible, although
slices are still handy when dealing with groups of values.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top