max
和min
函数每个元素只评估一次key
参数,我从他们引用的list.sort
文档中推断出来(以及有教养的猜测他们的实施):
与列表中每个项目对应的键计算一次,然后用于整个排序过程。
这意味着使用对于给定输入并不总是返回相同输出的键函数应该是安全的。但是,如果没有自定义功能或再次调用键功能,是否可以优雅地检索最大或最小的键?
对于非确定性密钥,以下内容不起作用:
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
的元素必须在迭代的相同元素上工作而不会相互干扰。 zip
确保tee
不必一次存储多个元素,以便在评估中获得最大的懒惰。 Enumeration确保对于键相同但值不同的情况,比较的稳定性以与原始函数一致的方式保留:
如果多个项目是最大[minimal],则该函数返回遇到的第一个项目。
请注意表达式中的减号最大化。
总而言之,这个函数看起来像检索已经计算过的东西一样有点过分。对此有更好的解决方案吗?
如果没有其他方法,至少此函数具有与max
相同的算法复杂度和一般契约。
正切/红利问题:什么是形容词含义"每次都没有为相同的输入返回相同的结果"?非确定性只是可能性的一小部分,不可重入意味着与我的理解略有不同。
答案 0 :(得分:4)
为此,您需要预先计算密钥。将键/值放在元组中可能是最有意义的。但是,您需要注意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))
答案 1 :(得分:2)
如何使用元组词典排序:
max_key, max_val = max((key(val), val) for val in iterable)
如果值不具有可比性,请comments建议:
max_key, _, max_val = max((key(val), -i, val) for i, val in enumerate(iterable))
如果keyfunc的结果是hashable:
d = {key(x): x for x in iterable} # note: last value wins for ties
max_key = max(d)
max_val = d[max_key]
答案 2 :(得分:0)
我相信这也应该有效:
max(((key(x),x) for x in iterable),key=lambda kx:kx[0])