PEP 284, Integer for-loops (fwd)

James_Althoff at i2.com James_Althoff at i2.com
Mon Mar 11 18:17:38 EST 2002


[James_Althoff at i2.com]
> ...
> But if your protocol is
>     def getRowCount(): pass
>     def getColumnCount(): pass
>     def getValueAt(rowIndex,columnIndex): pass
>     def setValueAt(rowIndex,columnIndex): pass
> then that option is not available.

[Tim Peters]
> Hmm.  The inability to pass a new value to setValueAt seems to make
> this a little feeble <wink>.

Ah, but is it feeble or is it *omniscient*?

Actually, I did it this way just to be consistent; I make the same mistake
when I type it in code, too.


The suggestions below are nice -- although our particular (Java-based)
context leaves us still with a few spanners to deal with.


> Depending on how much overhead you're willing to bear, it's
> straightfoward to write wrapper classes (even using the old
> for/__getitem__ protocol) such that, e.g., adding 1 to every
> element in a table could be expressed via
>
> t = Table(original_table)
> for r in t.rows():
>     for e in r.elements():
>         e.set(e.get() + 1)

This is nice.  We have noticed that there can be a hit in Jython the first
time you instantiate a Python subclass of a Java class (it seems to build
some internal structures based on a lot of Java reflection).  We could
implement a Java subclass instead.  But we are trying to minimize the
appearance of needing to resort back to Java for what are probably obvious
reasons.  But I think it might be worth having this option for times when
performance isn't an issue and looping through all rows or columns is
required.  I would rather do it with the new iterator stuff that is
available in C-Python, but ... oh well.

> Short of that, you could at least reduce the clumsiness by
> defining helper functions, like (assuming these are 0-based
> indices; fiddle to taste):
>
> def ri(table):
>     "Return row indices for table."
>     return range(table.getRowCount())
>
> def ci(table):
>     "Return column indices for table."
>     return range(table.getColumnCount())
>
> and then
>
> columns = ci(table)
> for i in ri(table):
>     for j in columns:
>          table.setValueAt(i, j, table.getValueAt(i, j) + 1)
>
> would save some typing yet be just as quick.  Add
>
> def getter(table):
>     "Return bound table.GetValueAt."
>     return table.GetValueAt
>
> def setter(table):
>     "Return bound table.SetValueAt."
>     return table.SetValueAt
>
> and then
>
> get, set = getter(table), setter(table)
> columns = ci(table)
> for i in ri(table):
>     for j in columns:
>          set(i, j, get(i, j) + 1)
>
> would be about as fast as you can get in Python, short of defining a
custom
> "add 1 to element and then store it back" function.

Some helper functions might be convenient.  They would be most convenient
in a helper utility module, of course, so that we could standardize them
for the entire development team.  A twist that we have to live with is that
the 3rd party components are not always consistent with each other.  JTable
and DefaultTableModel both use "getRowCount()".  So we could have a generic
function for these.  But another 3rd party component uses "getNumberOfRows
()" instead.  So we would have to do the try:except:/hasattr/isinstance
stuff to make the helper functions generic.  Probably worth giving it a
try.


> If you're very concerned about speed, when you've got a choice the loop
with
> the largest trip count should be innermost (as true in Python as in any
> other language, although this is clawing at what are usually third-order
> effects).

Thanks for the suggestions.

Jim





More information about the Python-list mailing list