comparing all values of a list to regex

Alex Martelli aleax at aleax.it
Fri Sep 27 03:09:37 EDT 2002


Delaney, Timothy wrote:
        ...
>> > elif matches != len(alist):
>> >     match_list = match_some
>> > 
>> > match_list.append(alist)
>> 
>> Yes, choosing a list first and then appending to the chosen
>> list is nicer, but you cannot do that within the original
>> specs (for all I know the match_some list might grow
>> unbearably large over time, in this case).
> 
> Hmm - why not? Ignoring that the original specs didn't actually *have*
> that case, 

Not ignoring it, one would think the specs must be to do nothing
special in that case.

If your program was specified to "append to list1 if the time is
exactly 11:22:33, to list2 if the time is exactly 17:26:35", would
you consider it reasonable to make your program appent to list3
if it's any other time of day?

The problem could be that the program is called a billion times,
of which, say, about seven fall in the first case, about three in
the second one, and all the rest in the "neither" category.  If you
extend the program's spec to append to another list when "neither"
applies, your program will overflow memory and crash, while any
proper implementation of the specs would work correctly.

> choosing the list to append to, or doing it in-place is merely
> an implementation detail. 

Except that, to meet the specs, you must NOT append to match_some,
ever.  It's quite possible of course that the specs where incorrect
or incomplete, but just assuming that seems strange to me.

> Of course, you would need instead to do something like:
> 
> match_list = None
> 
> if not matches:
>     match_list = match_none
> elif matches == 1:
>     match_list = match_one
> elif matches == len(alist):
>     match_list = match_all
> 
> if match_list is not None:
>     match_list.append(alist)

Right (I did much the same in my dictionary-version of this
snippet) although of course the need for the final if is not
most elegant.

A good design pattern for such cases is Null Object, which
Dinu Gherman showed in recipe:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205

To quote him,
"""
Null objects are intended to provide first-class citizens as
a replacement for the primitive value None. Using them you
can avoid conditional statements in your code and express
algorithms with less checking for special values.
"""

With this DP, your snippet would become:

import null
match_list = null.Null

if not matches:
    match_list = match_none
elif matches == 1:
    match_list = match_one
elif matches == len(alist):
    match_list = match_all

match_list.append(alist)


The need for the guard goes away because null.Null is
basically designed so you can call any method on it
innocuously.  Of course, you get more mileage out of
it in more complicated situations.

{0:match_none, 1:match_one, len(alist):match_all}.get(
    matches, null.Null).append(alist)

gets greater benefit wrt the None-plus-guard.  Of course,
this more-concise snippet has a problem when len(alist)
is 0 -- your snippet, like my original one, is not
ambiguous in this case (appends to match_none), while a
dictionary-display with two identical keys has behavior
that is less obvious (I don't recall whether the Python
language specifies which of the two repetitions of the
same key "takes", but in any case most readers and
maintainers of this code would be puzzled).  Fortunately,
the OP clarified (if I read his followup post correctly)
than len(alist)==0 is a "can't happen" in his case.


Alex




More information about the Python-list mailing list