range(2.9); coercions

John Machin machin_john_888 at hotmail.com
Tue Jul 31 18:33:42 EDT 2001


Does it concern anyone that range(2.9) *silently* returns [0, 1]?

A strict reading of the documentation suggests that it should return
[0, 1, 2] (because 2 is the largest integer < 2.9).

However my real concern is not with the behaviour of range() itself,
but with the *general* mechanism by which it gets that answer -- this
mechanism would have been used by a large proportion of all the C
extension modules ever written.

Unless the birds ate my breadcrumbs while I was stumbling around the
source for Python 2.1, what happens is this: the range()
implementation calls PyArg_ParseTuple, saying that it expects "i"
(integer) argument(s). This is however interpreted to include any
instance of a type that has an nb_int slot, or a class that has an
__int__() method.

So 2.9 is a float, the float type supplies an nb_int slot, and
int(2.9) -> 2 and so 2 is used as the open upper bound.

IMO any coercion that "narrows" (loses information) should only be
possible by explicit coding -- range() is intended to have integer
arguments; callers who know they have some float expression should be
required to "declare" and document this by using range(int(floater))
while callers who don't know but use range(floater) should be thrown
an exception just as surely as if they had used list[1.1:2.2]

This implicit undocumented behaviour of PyArg_ParseTuple worries me --
callers can "happily" pass float arguments where the module
implementor was confidently expecting that non-int arguments would be
fended off by PyArg_ParseTuple. Yes, the callers should test their
code, but having an exception instead of silent truncation would find
the problem earlier, and say explicitly what the problem was.



More information about the Python-list mailing list