[Python-ideas] Should range() == range(0)?

Terry Reedy tjreedy at udel.edu
Sun May 6 23:24:40 CEST 2012


It is a general principle that if a built-in class C has a unique (up to 
equality) null object, then C() returns that null object.

 >>> for f in (bool, int, float, complex, tuple, list, dict, set, 
frozenset, str, bytes, bytearray):
	print(bool(f()))

# 12 lines of False

Some imported classes such as fractions.Fraction and collections.deque 
can be added to the list.

I add 'up to equality' because in the case of floats, 0.0 and -0.0 are 
distinct but equal, and float() returns the obvious 0.0.
 >>> 0.0 == -0.0
True
 >>> m.copysign(1, 0.0)
1.0
 >>> m.copysign(1, -0.0)
-1.0
 >>> m.copysign(1, float())
1.0

The notable exception to the rule is
 >>> range()
Traceback (most recent call last):
   File "<pyshell#0>", line 1, in <module>
     range()
TypeError: range expected 1 arguments, got 0
 >>> bool(range(0))
False

It is true that there are multiple distinct null range objects (because 
the defining start,stop,step args are kept as attributes) but they are 
all equal.
 >>> range(1,1) == range(0)
True

range(0) == range(0, 0, 1) would be the obvious choice for range().

Another advantage of doing this, beside consistency, is that it would 
emphasize that range() produces a re-iterable sequence, not just an 
iterator.

Possible objections and responses:

1. This would slightly complicate the already messy code and doc for 
range().

Pass, for now.

2. There is little need as there is already the alternative.

This is just as true or even more true for the other classes. While 
int() is slightly easier to type than int(), 0 is even easier.

3. There is little or no use case.

The justification I have seen for all the other classes behaving as they 
do is expressions like type(x)(), which gets the null object 
corresponding to x. This requires a parameterless call rather than a 
literal (or display) or call with typed arg.

A proper objection this sort would have to argue that range() is less 
useful than all 12+ cases that we have now.

4. memoryview() does not work.

Even though memoryview(bytes()) and memoryview(bytearray()) are both 
False and equal, other empty memoryviews would not all be equal. Besides 
which, a memoryview is dependent on another object, and there is not 
reason to create any particular object for it to be dependent on.

5. The dict view methods, such as dict.keys, do not work.

These also return views that are dependent on a primary object, and the 
views also are null if the primary object is. Here there is a unique 
null primary object, so it would at least be possible to create an empty 
dict whose only reference is held by a read-only view. On the other 
hand, '.keys' is a function, not a class.

6. filter() does not work.

While filter is a class, its instances, again, are dependent on another 
object, not just at creation but during its lifetime. Moreover, 
bool(empty-iterable) is not False. Ditto for map() and, for instance, 
open(), even though in the latter case the primary object is external.

-- 
Terry Jan Reedy




More information about the Python-ideas mailing list