[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