Boolean tests [was Re: Attack a sacred Python Cow]
Heiko Wundram
modelnine at modelnine.org
Tue Jul 29 23:06:46 CEST 2008
Also, just a couple of points:
Am 29.07.2008, 22:27 Uhr, schrieb Carl Banks <pavlovevidence at gmail.com>:
> 1. Any container type that returns a length that isn't exactly the
> number of elements in it is broken.
I agree, but how do you ever expect to return an infinite element count?
The direction I took in that recipe was not returning some "magic" value
but raising an OverflowError (for example, you could've also cropped the
length at 2**31-1 as meaning anything equal to or larger). This is the
thing that breaks your explicit test for non-emptyness using len(x) > 0,
but it's also the only possible thing to do if you want to return the
number of elements exactly where possible and inform the user when not
(and OverflowError should make the point clear).
Anyway, that's why there is a separate member function which is explicitly
documented to return a magic value in case of an infinite set (i.e., -1)
and an exact element count otherwise, but using that (you could write
x.len() != 0 for the type in question to test for non-emptiness) breaks
polymorphism.
> 2. The need for __nonzero__ in this case depends on a limitation in
> the language.
True, but only for sets with are finite. For an infinite set, as I said
above: what would you want __len__() to return? There is no proper
interpretation of __len__() for an infinite set, even though the set is
non-empty, except if you introduced the magic value infinity into Python
(which I did as -1 for my "personal" length protocol).
> 3. On the other hand, I will concede that sometimes calculating len is
> a lot more expensive than determining emptiness, and at a basic level
> it's important to avoid these costs. You have found a practical use
> case for __nonzero__.
This is just a somewhat additional point I was trying to make; the main
argument are the two points you see above.
> However, I'd like to point out the contrasting example of numpy
> arrays. For numpy arrays, "if x" fails (it raises an exception) but
> "if len(x)!=0" succeeds.
>
> The only sane advice for dealing with nonconformant classes like numpy
> arrays or your interger set is to be wary of nonconformances and don't
> expect polymorphism to work all the time.
The thing is: my integer set type IS conformant to the protocols of all
other sequence types that Python offers directly, and as such can be used
in any polymorphic function that expects a sequence type and doesn't test
for the length (because of the obvious limitation that the length might
not have a bound), but only for emptiness/non-emptiness. It's the numpy
array that's non-conformant (at least from what you're saying here; I
haven't used numpy yet, so I can't comment).
> So I guess I'll concede that in the occasional cases with
> nonconformant classes the "if x" might help increase polymorphism a
> little.
>
> (BTW: here's another little thing to think about: the "if x" is useful
> here only because there isn't an explicit way to test emptiness
> without len.)
The thing being, again, as others have already stated: __nonzero__() IS
the explicit way to test non-emptiness of a container (type)! If I wanted
to make things more verbose, I'd not use "if len(x)>0", but "if bool(x)"
anyway, because "casting" to a boolean calls __nonzero__(). "if len(x)>0"
solves a different problem (even though in set theory the two are
logically similar), and might not apply to all container types because of
the restrictions on the return value of __len__(), which will always exist.
--- Heiko.
More information about the Python-list
mailing list