Boolean tests [was Re: Attack a sacred Python Cow]
giltay at gmail.com
giltay at gmail.com
Tue Jul 29 17:48:31 EDT 2008
On Jul 29, 1:30 pm, Carl Banks <pavlovevide... at gmail.com> wrote:
> On Jul 29, 5:15 am, Heiko Wundram <modeln... at modelnine.org> wrote:
>
> > I can't dig up a simple example from code I wrote quickly, but because of the
> > fact that explicit comparisons always hamper polymorphism
>
> I'm not going to take your word for it. Do you have code that
> demonstrates how "if x" improves polymorphism relative to simple
> explicit tests?
>
> Carl Banks
I think "if x" works in some cases, but not in others. Here's an
example I just made up and a counterexample from my own code.
Example:
Here's a function, print_members. It's just something that takes some
iterable and prints its members in XML. It's special-cased so that an
empty iterable gets an empty tag. (This is a bit of a trivial
example, I admit; the point is that the empty iterable is a special
case.)
def print_members(iterable):
if not iterable:
print '<members />'
return
print '<members>'
for item in iterable:
print '<item>%s</item>' % item
print '</members>'
>>> print_members(['a', 'b', 'c'])
<members>
<item>a</item>
<item>b</item>
<item>c</item>
</members>
>>> print_members([])
<members />
I could have used "if len(iterable) == 0:" for these cases, but only
because the length of a list means something. Say I were to implement
a matrix class. The length of a matrix isn't well-defined, so I won't
implement __len__. I will, however, implement __nonzero__, since if
it has no members, it's empty.
class SimpleMatrix(object):
def __init__(self, *rows):
self.rows = rows
def __nonzero__(self):
for row in self.rows:
if row:
return True
return False
def __iter__(self):
return iter(sum(self.rows, []))
>>> a = SimpleMatrix([1, 2, 3], [4, 5, 6])
>>> print_members(a)
<members>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
</members>
>>> b = SimpleMatrix([],[],[])
>>> len(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'SimpleMatrix' has no len()
>>> print_members(b)
<members />
So print_members can work on iterables that have no len(), and handle
the special case of an empty iterable, as long as __nonzero__ is
implemented.
Counterexample:
While "if x" works well in some circumstances, I don't like using it
for purely numeric types. For instance, I have a mutable Signal class
for doing some basic DSP. Among other things, I can apply a DC offset
to the signal (it just adds the offset to all the samples). I have a
special case for an offset of 0 so I don't have to loop through all
the samples (I also have a two-pass remove_offset method that
subtracts the average; if it's already properly centred, I can skip a
step).
class Signal:
[...]
def dc_offset(self, amount):
if amount == 0:
return
self.samples = [sample + amount for sample in self.samples]
Here, "if amount == 0" is deliberate. At no point should I be adding
an offset that's a list or a dict, even an empty one.
Signal.dc_offset should raise an exception if I try to do that,
because that indicates there's a bug somewhere. If I do pass in [] or
{}, that test will fail, and it will try to add the list or dict to
the samples, at which point I get a TypeError.
Geoff G-T
More information about the Python-list
mailing list