PEP 284, Integer for-loops (fwd)

Tim Peters tim.one at comcast.net
Tue Mar 12 04:33:01 CET 2002


[Tim]
>> 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)

[James Althoff]
> 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).

I didn't have subclassing in mind; e.g.,

class Table:
    def __init__(self, table):
        self.t = table

    def rows(self):
        return RowIterator(self.t)

    def cols(self):
        return ColIterator(self.t)

class Element:
    def __init__(self, table, i, j):
        self.t, self.i, self.j = table, i, j

    def get(self):
        return self.t.getValueAt(self.i, self.j)

    def set(self, value):
        self.t.setValueAt(self.i, self.j, value)

purely in Python.  Your original table type is incorporated by containment
here, not subclassing.  Now you just need to define RowIterator and
ColIterator.  For example, picking on rows, and in a way that makes "a row"
an object in its own right:

class RowIterator:
    def __init__(self, table):
        self.t = table
        # Unclear whether these guys are 0-based or 1-based.
        # This assumes 0-based.  Fiddle accordingly.
        self.limit = table.getRowCount()

    def __getitem__(self, i):
        if 0 <= i < self.limit:
            return Row(self.t, i)
        else:
            raise IndexError

class Row:
    def __init__(self, table, i):
        self.t, self.i = table, i

    def get(self, j):
        return self.t.getValueAt(self.i, j)

    def set(self, j, value):
        self.t.setValueAt(self.i, j, value)

    def elements(self):
        return RowElementIterator(self.t, self.i)

class RowElementIterator:
    def __init__(self, table, i):
        self.t, self.i = table, i
        self.limit = table.getColumnCount()

    def __getitem__(self, j):
        if 0 <= j < self.limit:
            return Element(self.t, self.i, j)
        else:
            raise IndexError

That implements more than the example at the start actually needs; cut back
to taste <wink>.

> 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.

The new iterator stuff makes coding this kind of thing clearer and easier,
but the visible *interface* need not change at all.  This is actually
well-suited to the __getitem__ protocol:  that was designed with sequential
indexing in mind, and that's what you're doing here  The new iterator
protocol is an enormous improvement only when sequential indexing is a
strain (like, e.g., in tree traversal).

> ...
> 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.

I'd try a dict, mapping types to unbound methods, like:

rowcounter = {JTable: JTable.getRowCount,
              DefaultTableModel: DefaultTableModel.getRowCount,
              ThirdPartyBozo: ThirdPartyBozo.getNumberOfRows}

def ri(table):
    "Return indices of table rows."
    return range(rowcounter[type(table)](table))

I don't know whether that works exactly as-is in Jython, but you get the
idea.  Unless you have an unbounded number of classes (a bad Smalltalk habit
you should have given up by now <wink>), using dicts to add "conceptual
members" to classes is an effective technique.

there's-more-than-one-to-do-indirection-in-python-ly y'rs  - tim





More information about the Python-list mailing list