faqts : Computers : Programming : Languages : Python : Snippets

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

4 of 7 people (57%) answered Yes
Recently 1 of 4 people (25%) answered Yes

Entry

enumeration/iteration wrappers/idioms

Jul 5th, 2000 10:03
Nathan Wallace, Hans Nowak, Snippet 371, Alex Martelli


"""
Packages: basic_applications.enumeration
"""
"""
Problem 1: we often use the long-winded idiom
    while 1:
        nextitem=anobject.somemethod()
        if not nextitem:
            break
        # proceed to process nextitem
where we'd really like to use a tighter syntax
for the _same_ semantics (e.g., the kind of
"while(nextitem=anobject.somemethod())"
kind of thing that C/C++ allow since, there,
assignment is an expression, not a statement).
Solution 1: use a wrapper class idiom:
    for nextitem in enum(anobject.somemethod):
        # proceed to process nextitem
where:
"""
class enum:
    def __init__(self,stepper):
        self.stepper=stepper
    def __getitem__(self,key):
        item = self.stepper()
        if not item: raise IndexError
        return item
"""
This can easily be generalized to cases where
the stepper method needs arguments (using
"apply" to get the argument tuple), cases where
a separate "tester" method is needed (record
it in the enum object, use it instead of "not item"),
etc.
Problem 2: we often want both the key and
value from within a sequence, and some dislike:
    for key in range(len(sequence)):
        value = sequence[key]
        # proceed to process key and value
Solution 2: use a wrapper class idiom:
    for key,value in kv_enum(sequence):
        # proceed to process key and value
where:
"""
class kv_enum:
    def __init__(self,seq):
        self.seq=seq
    def __getitem__(self,key):
        return (key,self.seq[key])
"""
This can easily be generalized to parallel
stepping along several sequences, with
whatever semantics you wish if they differ
in length -- just record them all in the
initializer, index them all in getitem.
Problem 3: a very general wrapper adds some
overhead at each __getitem__, e.g.:
"""
class enum:
    def __init__(self,stepper,tester=None):
        self.stepper=stepper
        self.tester=tester
    def __getitem__(self,key):
        item = self.stepper()
        if tester:
            if not self.tester(item): raise IndexError
        else:
            if not item: raise IndexError
        return item
"""
This is slightly slower although more general
than the above, simpler wrapper, since a further
if must be performed at each getitem.
Solution 3: make enum into a function that
will return an object from an _appropriate_
class (the various classes need no special
generality, each can be optimized).  E.g.:
"""
class enum0:
    def __init__(self,stepper):
        self.stepper=stepper
    def __getitem__(self,key):
        item = self.stepper()
        if not item: raise IndexError
        return item
class enum1:
    def __init__(self,stepper,tester):
        self.stepper=stepper
        self.tester=tester
    def __getitem__(self,key):
        item = self.stepper()
        if not self.tester(item): raise IndexError
        return item
def enum(stepper,tester=None):
    if tester: return enum1(stepper,tester)
    else: return enum0(stepper)
"""
Here, the "if" is only needed _once_, when a
wrapper is created; then, each wrapper, used
polymorphically, can be fully specialized.
This technique can be used more generally
than just for wrapper classes: rather than one
class which needs an "if" per method call to
find out how it was built, make several classes,
each specialized to one way of building and
operating it, and a "factory" builder-function
that chooses which specialized class it needs
to build and return to the caller.  Hey presto --
ONE "if" per object constructed, rather than
one per call onto its methods!-)
"""