Furthermore, I think that if Python publicly admit in its documentation that
min()have an “undefined behavior” only if the first elements of the iterable does not have well-defined behavior of comparison operators, I think that any developers in the world will laugh
Furthermore, I think that if Python publicly admit in its documentation that
Marco: please could you keep the discussion respectful?
list.sort() and any other function like them, I think the current behavior is correct. Indeed the result is completely undefined if the NaN changes its position, it’s not only a problem if it’s at the start.
Maybe Python could implement total ordering by default for numbers? The problem is total ordering requires that NaNs carry on a payload. I suppose that Python does not store this information, so it could be really a problem to implement it, and I don’t know how much is useful in real world.
max() should ignore unorderable elements, and
list.sort() raise a warning.
sorted, there’s some relevant recent discussion on the tracker (see issue 36095). The root cause of these issues really has nothing to do with
max; rather, it stems from the way comparisons on NaNs work. Changing those comparisons to implement a total order (or at least a total preorder), or to raise on comparisons involving NaNs, seems reasonable on the face of it. But that would involve significant breakage of existing code. It’s yet another of those situations where the answer to the question “Should we have done this differently?” is “Possibly, yes.”. But that’s not helpful, because the question actually facing us is “Should we change it now?”, and that’s a very different proposition.
@mdickinson I don’t think this change will break nothing:
- raising a warning let the code run. Simply the coder will be informed of the problem. “Errors should never pass silently.”
- ignoring first NaNs in
mincould fix some code, instead As I already said, probably the coders that encountered the problem have removed the NaNs from the iterable, or used
numpy. Who never fixed it, it’s quite improbable that the code after
minreally expects NaN. So Python probably will fix that codes silently.
Anyway, there’s an alternative.
Python could implement total ordering only partially:
- +NaN > number == True
- number > -NaN == True
- +NaN >= +NaN == False
- +NaN <= +NaN == False
- -NaN >= -NaN == False
- -NaN <= -NaN == False
Pro: sorting will be no more undefined if NaN is present
max will return NaN if present, at any position.
min could return -NaN, but they are very rare.
In these cases, I think
min could raise a warning.
Anyway, in this case, this is a real change in Python, and should be handled by a PEP. That I have no time to open
@mdickinson: anyway, if you want my opinion, for what it’s worth, my check is more simple and works for any object that does not support ordering, not only for NaNs.
And IMHO it should be the default for
bisect, and all the other folks because, as I said, it’s quite improbable that this change will break something, but, on the contrary, it will silently fix many old codes.
If someone wants total ordering, a
math.total_ordering can be implemented and passed as key for sorting.
My 2 cents.
@Marco_Sulla not only would this break the IEEE-574
NaN specification, this entire topic has nothing to do with adding
minmax to Python. I respectfully ask you to open another thread if you want to continue the discussion on how
NaNs should be handled in Python.
I forked the discussion here: https://discuss.python.org/t/2868
@ruud Anyway, if you want to support also
default, I think that, before you return it, you should transform it to a
tuple and check if its length is 2. Otherwise, an error should be raised.
Obviously not as evident as you presume, from the reactions to ypur example.
Given the “two different placements of NaN” example, I would think its the fault of trying to find the minimum of results containing NaN’s. I would be at fault for not accounting for NaN’s. Ignoring them, or any other action could depend on circumstance - wh’s to say that an arbitrary resolution is th one needed?
It might be better still to raise an exception unless an option is given to state how NaN’s should be treated - at least they won’t silently be ignored.
Marco, point us at official Python documentation that documents what min
and max are supposed to do when passed values that don’t provide a total
order, and if that documented behaviour is different from what the
functions actually do, then we will conceed that it is a bug.
There are at least three different things that min(0, NAN) could do:
- propogate the NAN (return a NAN)
- ignore the NAN (return 0)
- raise an exception
and no consensus on what it should do. At least half our users will
consider your version to be “buggy” since it doesn’t match their
expectations. You don’t get to unilaterally decide what is correct and
what is buggy.
If you want to change this, you will need to write a PEP. You should
consider the two different versions of min/max that the IEEE-754
standard specifies, plus any other versions that others may desire
(such as a version which raises an exception).
It isn’t enough to specify the behaviour with NANs, there are an
infinite number of ways a data set can fail to provide a total order.
For instance, dominance heirarchies are often not a total order, for
example Rock Paper Scissors:
Rock > Scissors Scissors > Paper Paper > Rock max(Rock, Paper, Scissors) should do what?
Unless you are prepared to write a PEP, there’s no point in you
continuing this argument. There is no agreement on what the “correct”
behaviour is, so any version we provide will surprise some people. The
best we can do is explicitly document that the behaviour for data sets
that don’t define a total order will be implementation-defined and
therefore we make no promise about what will happen.
(The fact that min and max currently ignore NANs that aren’t in the
first position is an accident, not an intentional behaviour.)
Paul: “undefined behaviour” has special meaning to C programmers which
it doesn’t have to other language programmers. Given the special place C
has in the programming ecosystem, I prefer to honour their definition,
and use “implementation-defined behaviour” for what we are talking about
Thanks, that’s a good point. I do like the connotations of C’s “undefined behaviour” (in general, but in this discussion in particular) but if it’s not a familiar idea to people in general, it’s probably just confusing the discussion. I’ll keep this in mind.
As a reminder to people, posts can be flagged to the admins to handle potential PSF Code of Conduct issues so they can be dealt with appropriately.
Please Mark, “even a baby can do it” and similar phrases is a standard
English idiom for “its easy”. Its no more disrespectful than if Marco
had said “It is easy to understand that code”.
It is not an aggression, not even a microagression or a nanoaggression,
although I suppose the mere fact that Marco is disagreeing with me makes
it a picoaggression. I think I can cope with mere disagreement wink
Can we please assume good faith and not be so touchy that everyone has
to walk around on eggshells for fear of being banned for the most
It should not only restrict to find min max numbers, other functionalities also include e.g comparisons of min max of two lists, string lengths etc. It will helpful in spatial data as well.
I don’t want this thread to devolve into a CoC discussion (or really get into one period), but the issues went beyond just the “baby” comment. And in regards to that specific comment, it could still be insulting to someone who actually didn’t find it easy to understand and so feels like a put-down. Simply saying “I think it’s easy to understand” would have been enough to get the point across and not risk insulting someone accidentally.
There’s something of a parallel with the built-in function
divmod here. There’s an excellent reason for having
divmod available, namely that it’s common to want to compute both
a // b and
a % b as part of some algorithm, and computing them separately is needlessly inefficient: in current CPython,
a // b (for
b of type
int) computes both the quotient and the remainder, then throws the remainder away and returns the quotient. Similarly for
a % b. So by evaluating
a // b and
a % b separately you’re computing the quotient and the remainder twice each instead of once.
divmod solves this inefficiency.
Nevertheless, I’ve always found
divmod oddly unsatisfying in practice, and despite writing a lot of integer-based algorithmic code, I almost never use
divmod, and I rarely see it used in other people’s code. Part of the issue is readability: if I have an expression that uses both
a % b and
a // b and I want to use
divmod for efficiency, I have to introduce a separate statement to call
divmod(a, b) and unpack the resulting tuple. I can’t simply use
divmod directly in my expression without using it twice, which defeats the point. So my functional-style code becomes more procedural and a mite harder to read as a result, especially since a function call is being substituted for operators.
I don’t know if we have, or can get, statistics on usage of builtins, but I’d hazard a guess that
divmod is one of the least-used built-in functions. (To be clear, before anyone gets excited, I am not proposing that
divmod be deprecated, moved, or removed. It is what it is.)
minmax would suffer from some of the same issues, though we don’t have the operator versus function call aspect. I think it wouldn’t get used as much as one might a priori expect. It might be useful to look at some before-and-after examples from real-world code to see to what extent readability is affected.
That’s not to say that
minmax wouldn’t be useful, but in spite of
max being builtins, I doubt that
minmax would be useful often enough to justify adding it to builtins. Which leaves us with the issue of figuring out where it does belong, and I don’t have a good answer to that.
itertools seem like the obvious candidates. Of the three, I’d probably go with
math: it bothers me a bit that
max, wouldn’t necessarily be specifically for numbers, which makes
math seem inappropriate, but we already have a precedent for putting a generic not-necessarily-numeric function in
math, in the form of
Out of idle curiosity how many of these statistical functions would there be that would benefit from a “continuous” evaluation of their value? (Ignoring the discussion on NAN for a moment)
Would there be value in a function that would evaluate a range of statistical values in a single live pass?
Possibly returning a tuple in the order that the functions were requested?