Terry Reedy wrote:
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
I don't think that's so much a general principle that should be aspired to as a general observation that many objects have an obvious "nothing" (empty) value that intuitively matches the zero-argument case, e.g. set, dict, list and so forth. The cases of int, float, complex etc. are a little more dubious; I'm not convinced there's a general philosophical reason why int() should be allowed at all. E.g. int("") fails, int([]) fails, etc. so there's no general principle that the int of "emptiness" is expected to return 0. The fact that float() has to choose between two zero objects, complex() between four, and Fraction and Decimal between an infinity of zero objects, highlights that the choice of a "default" is at least in part an arbitrary choice. If Python has any general principle here, it is that we should be reluctant to make arbitrary choices in the face of ambiguity. For the avoidance of doubt, I'm not arguing for changing the behaviour of int. The current behaviour is fine. But I don't think we should treat it as a general principle that other objects should necessarily follow.
Some imported classes such as fractions.Fraction and collections.deque can be added to the list. [...] 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
Are you using Python 2 here? If so, you should be looking at xrange, not range. In Python 3, range objects are equal if their start, stop and step attributes are equal, not if their output values are equal: py> range(0) == range(1,1) False py> range(1, 6, 2) == range(1, 7, 2) False
range(0) == range(0, 0, 1) would be the obvious choice for range().
I'm not entirely sure that is quite so obvious. range() defaults to a start of 0 and a step of 1, so it's natural to reason that range() => range(0, end, 1). But surely we should treat end to be a required argument? If end is not required, that suggests the possibility of calling range with (say) a start value only, using the default end and step values. I think there is great value in keeping range simple, and the simplest thing is to keep end as a required argument and refuse the temptation to guess if it is not given. I do think this is a line-call though. If I were designing range from scratch, I too would be sorely tempted to have range() => range(0).
Another advantage of doing this, beside consistency, is that it would emphasize that range() produces a re-iterable sequence, not just an iterator.
I don't follow your reasoning there. Whether range(*args) succeeds or fails for some arbitrary value of args has no bearing on whether it is re-iterable. Consider zip().
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.
Likewise reversed() and iter(). sorted() is an interesting case, because although it returns a list rather than a (hypothetical) SortedSequence object, it could choose to return [] when called with no arguments. I think it is right to not do so. zip() on the other hand is a counter-example, and it is informative to think about why zip() succeeds while range() fails. zip takes an arbitrary number of arguments, where no particular argument is required or treated differently from the others. Also there is a unique interpretation of zip() with no arguments: an empty zip object (or list in the case of Python 2). Nevertheless, I consider it somewhat surprising that zip() succeeds, and don't think that it is a good match for range. Given the general principle "the status quo wins", I'm going to vote -0 on the suggested change. -- Steven