for-looppython

Pythonic ways to use 'else' in a for loop


I have hardly ever noticed a python program that uses else in a for loop.

I recently used it to perform an action based on the loop variable condition while exiting; as it is in the scope.

What is the pythonic way to use an else in a for loop? Are there any notable use cases?

And, yea. I dislike using break statement. I'd rather set the looping condition complex. Would I be able to get any benefit out of it, if I don't like to use break statement anyway.

Worth noting that for loop has an else since the language inception, the first ever version.


Solution

  • What could be more pythonic than PyPy?

    Look at what I discovered starting at line 284 in ctypes_configure/configure.py:

        for i in range(0, info['size'] - csize + 1, info['align']):
            if layout[i:i+csize] == [None] * csize:
                layout_addfield(layout, i, ctype, '_alignment')
                break
        else:
            raise AssertionError("unenforceable alignment %d" % (
                info['align'],))
    

    And here, from line 425 in pypy/annotation/annrpython.py (clicky)

    if cell.is_constant():
        return Constant(cell.const)
    else:
        for v in known_variables:
            if self.bindings[v] is cell:
                return v
        else:
            raise CannotSimplify
    

    In pypy/annotation/binaryop.py, starting at line 751:

    def is_((pbc1, pbc2)):
        thistype = pairtype(SomePBC, SomePBC)
        s = super(thistype, pair(pbc1, pbc2)).is_()
        if not s.is_constant():
            if not pbc1.can_be_None or not pbc2.can_be_None:
                for desc in pbc1.descriptions:
                    if desc in pbc2.descriptions:
                        break
                else:
                    s.const = False    # no common desc in the two sets
        return s
    

    A non-one-liner in pypy/annotation/classdef.py, starting at line 176:

    def add_source_for_attribute(self, attr, source):
        """Adds information about a constant source for an attribute.
        """
        for cdef in self.getmro():
            if attr in cdef.attrs:
                # the Attribute() exists already for this class (or a parent)
                attrdef = cdef.attrs[attr]
                s_prev_value = attrdef.s_value
                attrdef.add_constant_source(self, source)
                # we should reflow from all the reader's position,
                # but as an optimization we try to see if the attribute
                # has really been generalized
                if attrdef.s_value != s_prev_value:
                    attrdef.mutated(cdef) # reflow from all read positions
                return
        else:
            # remember the source in self.attr_sources
            sources = self.attr_sources.setdefault(attr, [])
            sources.append(source)
            # register the source in any Attribute found in subclasses,
            # to restore invariant (III)
            # NB. add_constant_source() may discover new subdefs but the
            #     right thing will happen to them because self.attr_sources
            #     was already updated
            if not source.instance_level:
                for subdef in self.getallsubdefs():
                    if attr in subdef.attrs:
                        attrdef = subdef.attrs[attr]
                        s_prev_value = attrdef.s_value
                        attrdef.add_constant_source(self, source)
                        if attrdef.s_value != s_prev_value:
                            attrdef.mutated(subdef) # reflow from all read positions
    

    Later in the same file, starting at line 307, an example with an illuminating comment:

    def generalize_attr(self, attr, s_value=None):
        # if the attribute exists in a superclass, generalize there,
        # as imposed by invariant (I)
        for clsdef in self.getmro():
            if attr in clsdef.attrs:
                clsdef._generalize_attr(attr, s_value)
                break
        else:
            self._generalize_attr(attr, s_value)