-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Behavior of the min/max with key=None #78330
Comments
I was faced with the fact that the behavior of the functions "min"/"max" and "sorted" is a little different. For example, this code works fine: >>> sorted([3, 2, 1], key=None)
[1, 2, 3] But the same example for "min" and "max" doesn't work: >>> min([3, 2, 1], key=None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable That is why the heapq library has this code:
At the same time, there are many places where such checks are not performed for the "sorted" function. I think the behavior of the "min" / "max" / "sorted" functions should be unified. That is, they should work as if "None" is the default value for "key". |
Accepting None has a drawback. It can hide a bug when the key function unexpectedly becomes None. |
+0 Probably users will never care about this but I don't see a downside beyond the small API churn. |
Whouldn't be better to add operator.identity and use it as the default value instead of None? |
No, that would incur overhead that currently isn't present. |
Accepting None makes the typing model more complex. Instead of just a callable functions accept callable-or-none. It terms of annotations, it is Union[Callable[[Any], Any], None] instead of just Callable[[Any], Any]. |
Serhiy, feel free to reject this PR. |
In 2.x, map(None, 'abc', 'zyz') == [('a', 'z'), ('b', 'y'), ('c', 'z')], but with the addition of zip, so that zip('abc', 'xyz') has the same result, we deprecated that use of None to mean identity function. For python-coded functions, a default is needed to make a keyword-only argument optional, and preferred over use of *args for making positional arguments optional. Unless I am missing something, a function can special-case 'key is identity', to avoid overhead, just as well as it can special-case 'key is None'. So rather than extend 'key=None', to me a kludge, I would rather replace it with 'key=identity'. Both can be accepted during a deprecation period. For instance, after adding identity, def nsmallest(n, iterable, key=identity):
...
if key is identity or key is None: # key in (identity, None)
result = min(it, default=sentinel)
... Since no one need ever write key=None, explicit passing should be rare. It seems to me that the main reason for the type declaration of key to include None is so that the def statement itself passes a consistency check with the None default. Once that is changed, most people should be able to use a simple callable declaration. I am considering this for python-ideas. Since the weekly issues list came out just 10 hours ago, I will not close this yet, but I will if still open in couple of days and no coredev objections. |
FWIW, the nsmallest/largest key param was added Dec 2, 2004, before keyword-only parameters. |
Repeating my comment on the Github PR, "I'm persuaded by your idea that min(), max(), nsmallest(), nlargest(), sorted(), and itertools.groupby() should ideally have the same API for key functions. Syncing of those APIs would be a minor improvement. As Serhiy pointed out, this does complicate the type signature a bit, but that is of small concern giving that the other four functions listed have already gone down this path." |
Alexander, thanks for the suggestion and patch. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: