【问题标题】:What is a pythonic way of using multiple fields for key evaluation during sorting?在排序期间使用多个字段进行键评估的pythonic方法是什么?
【发布时间】:2021-05-25 03:48:48
【问题描述】:

Python3 删除了在排序时用于比较的cmp parameter,并将其替换为key

cmp_to_key function 被引入“主要用作从 Python 2 转换的程序的转换工具”。

我不清楚如何使用key 在python3 中本地重新创建cmp 行为。例如,如果我想按长度对字符串进行排序,并按字典顺序使用决胜局,我会这样写:

def compare(word1, word2):
    if len(word1) > len(word2):
        return 1
    if len(word2) > len(word1):
        return -1
    if word1 < word2:
        return 1
    if word2 < word1:
        return -1
    return 0

myStrings.sort(key=functools.cmp_to_key(compare), reverse=True)

如何以 python3-native 方式进行对象之间的比较,这意味着没有cmp_to_key

我能想到的一件事是返回一个更复杂的键,例如:

# This would return something like 5.12 for "hello" and 5.20 for "world"
def myKey(word):
    return len(word) + myLexicographicalValueFunction(word)/100

myStrings.sort(key=myKey, reverse=True)

但是当您添加更多变量进行比较时,这似乎不能很好地扩展。在python3中排序期间使用多个字段进行键评估的pythonic方法是什么?

【问题讨论】:

  • 简而言之,“pythonic”的方式是让 key 函数返回一个值元组——每个值对应一个被比较的值——让 Python 的元组比较逻辑来处理。跨度>

标签: python python-3.x


【解决方案1】:

对于特定的比较,您可以使用元组作为键:

myStings.sort(key=lambda s:(-len(s),s),reverse=True)

对于对象类,您可能需要实现一个比较运算符(例如def __lt__(self,other): 方法)以让排序知道如何在本机比较它们。

如果您无法修改对象类或者您的比较非常复杂,您可以创建一个包装器对象,该对象使用您提供的 lambda 实现 __lt__() 方法:

class ObjectComp:
    def __init__(self,instance,cmp):
        self.instance = instance
        self.cmp      = cmp
        
    def __lt__(self,other):
        return self.cmp(self.instance,other.instance)
        
def cmpsort(objects,cmp):
    return [obj.instance for obj in sorted(ObjectComp(o,cmp) for o in objects)]


s = cmpsort([1,11,21,12,13],cmp=lambda a,b:a%10>b%10)
print(s)
[13, 12, 1, 11, 21]

【讨论】:

  • 1.啊,所以解决方案是一个包含尽可能多的字段的元组来打破平局? 2.对于对象,是否还需要实现gteq等才能工作?
  • @fvrghl 1:是的。 2:不需要,因为 key 可调用(此答案中的 lambda)包含决定顺序的逻辑。它将使用那个元素,比如e,为每个元素创建一个元组(-len(e), e),然后进行相应的排序。这就是您的排序所需的gteqlt 逻辑。我认为您仍然需要实现那些gteqlt 才能与有序集合一起使用。
【解决方案2】:

如果你有一个复杂的比较,你正在对字符串应用一个独特的含义,在 python 术语中可能意味着它会从它自己的类型中受益。 Python 很高兴允许您对泛型类型进行子类化,因此在您的情况下,您可能想要这样的东西:

class InterestingWord(str):

    def __lt__(self, x: str) -> bool:
        if len(self) < len(x):
            return True
        return super().__lt__(x)

    def __gt__(self, x: str) -> bool:
        if len(self) > len(x):
            return True
        return super().__gt__(x)

因此:

In [2]: InterestingWord("00") > InterestingWord("1")
Out[2]: True

In [3]: "00" > "1"
Out[3]: False

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2019-10-05
    • 1970-01-01
    • 2021-05-12
    相关资源
    最近更新 更多