Conditional Expressions don't solve the problem

Andrew Dalke dalke at dalkescientific.com
Wed Oct 17 09:15:42 CEST 2001


Dale Strickland-Clark wrote:
>Having not seen this before, I'm a bit surprised at the choice of the
>word 'else' here. It's not an alternative. It's a conclusion. I would
>have thought 'then' or 'finally' would have been more appropriate.

'finally' doesn't work because that implies it would be run
no matter what.  (The finally clause of an exception is always run.)

'then' adds a new keyword.

'else' is acceptable with a bit of a stretch - consider

for i in range(10):
  if f(i):
    break
else:
  g()

This is equivalent to

i = 0
while 1:
  if i < 10:
    if f(i):
      break
  else:
      g()
  i = i + 1

>A bit late now, I guess.

That too.

>Is iter just an extra object layer to map the interface. Will it be an
>overhead?

See
 http://python.sourceforge.net/devel-docs/lib/built-in-funcs.html#l2h-194
and
 http://python.sourceforge.net/devel-docs/lib/typeiter.html

I don't know what the overhead is.  There's different factors involved.
Your code used an attribute lookup (self.nextTerm), a function call,
and a test for truth at every step.

Using an iter does the attribute lookup once (holding self.nextTerm)
then each time through the loop does a lookup (for .next), a call
(of 'next'), which calls the function (but not lookup overhead),
then does a test for equality to the sentinel.

Call overhead is more than attribute lookup overhead, so this is
slower.  But you would be wise to test that.

However, suppose you rewrite your class so there is a 'next'
method which is almost the same as nextTerm except that it raises
a StopIteration exception at the end instead of returning None.

  def next(self):
      # This method meets the requirements of an iterator
      ...  # what you did before, except for how to stop ...
      raise StopIteration

  def nextTerm(self):
      # backwards compatibility
      try:
          return self.next()
      except StopIteration:
          return None

   def __iter__(self):
       # Return a 2.2-style iterator.  This method is also required
       # to be an iterator.
       return self

Then you'll be able to do

  for term in self:
      for a in argObjects:
          if a.test(term):
              break
      else:
          raise whatever

In this case (I think) there's only one callable and no additional
attribute lookup after initialization so it should have less
overhead than what you have now.  There isn't even the comparison
test because the end-of-iteration check is built into the overhead
of checking for exceptions.

It's also cleaner code, since it doesn't require the explicit
sentinel or iter call.

But it uses new magic methods, which I didn't want to get
into in my previous response.

You asked :)

                    Andrew
                    dalke at dalkescientific.com






More information about the Python-list mailing list