max
和
min
函数只为每个元素评估一次关键参数,我从
list.sort
的文档中推断出它们所引用的(以及对其实现的有根据的猜测):
The key corresponding to each item in the list is calculated once and then used for the entire sorting process.
这意味着使用一个并不总是为给定输入返回相同输出的键函数应该是安全的.但是,如果没有自定义功能或再次调用键功能,是否可以优雅地检索最大或最小的键?
对于非确定性密钥,以下内容不起作用:
max_val = max(iterable, key=key)
max_key = key(max_val)
出现同样的问题
max_val = sorted(iterable, key=key)[0]
自定义函数可以这样写:
from itertools import tee
def max_and_key(iterable, *, key=None):
i1, i2 = tee(iterable)
max_val = max(k, -i, v for i, (k, v) in enumerate(zip(map(key, i1), i2)))
return max_val[2], max_val[0]
tee
对于任意迭代都是必要的,其中zip
的元素必须在迭代的相同元素上工作而不会相互干扰.拉链确保发球台不必一次存放多个元素,以便在评估中获得最大的懒惰. Enumeration确保对于键相同但值不同的情况,比较的稳定性以与原始函数一致的方式保留:
If multiple items are maximal [minimal], the function returns the first one encountered.
请注意表达式中的减号最大化.
总而言之,这个函数看起来像检索已经计算过的东西一样有点过分.有更好的解决方案吗?
如果没有其他方法,至少此函数具有与max相同的算法复杂度和一般契约.
切线/红利问题:形容词的意思是“每次都没有为相同的输入返回相同的结果”?非确定性只是可能性的一小部分,而非可重入意味着与我的理解略有不同.
最佳答案 为此,您需要预先计算密钥.将键/值放在元组中可能是最有意义的.但是,您需要注意min / max / sort只对键而不是值执行比较(否则,如果值不可比,则如果存在重复键则将失败):
from operator import itemgetter
def max_with_key(iterable, key):
"""
Returns a (max_key, max_value) tuple by applying max to the iterable with
the given key. Useful in cases when the key function is non-deterministic
and the original key used in the max operation is desired.
>>> from random import randint
>>> max_with_key([1, 2, 3], key=lambda _: randint(0, 10))
(9, 3)
>>> max_with_key([1, 2, 3], key=lambda _: randint(0, 10))
(8, 1)
"""
prekeyed = ((key(x), x) for x in iterable)
return max(prekeyed, key=itemgetter(0))