functools total_ordering 似乎对继承的类没有任何作用

问题描述

我正在尝试以使用特殊比较的方式对字符串列表进行排序。我正在尝试使用 functools.total_ordering,但我不确定它是否正确填写了未定义的比较。

我定义的两个(> 和 ==)按预期工作,但 a > b 和 a < b。这怎么可能?我认为 total_ordering 会简单地将 < 定义为 not > and not ==。我的 < 测试结果是您通过常规 str 比较得到的结果,这让我相信 total_ordering 没有做任何事情。

也许问题是我继承了已经实现了 __lt__ 的 str ?如果是这样,是否有解决此问题的方法

from functools import total_ordering

@total_ordering
class SortableStr(str):

    def __gt__(self,other):
        return self+other > other+self

    #Is this necessary? Or will default to inherited class?
    def __eq__(self,other):
        return str(self) == str(other)

def main():

    a = SortableStr("99")
    b = SortableStr("994")

    print(a > b)
    print(a == b)
    print(a < b)

if __name__ == "__main__":
    main()

输出

True
False
True

解决方法

您说得对,内置的 str 比较运算符会干扰您的代码。来自the docs

给定一个定义一个或多个丰富的比较排序方法的类,这个类装饰器提供其余的。

所以它只提供那些尚未定义的。在您的情况下,total_ordering 无法检测到其中一些在父类中定义的事实。

现在,我们可以深入挖掘 source code 并找到确切的检查

roots = {op for op in _convert if getattr(cls,op,None) is not getattr(object,None)}

因此它检查值是否等于在根对象 object 中定义的值。我们可以做到这一点

@total_ordering
class SortableStr(str):

    __lt__ = object.__lt__
    __le__ = object.__le__
    __ge__ = object.__ge__

    def __gt__(self,other):
        return self+other > other+self

    #Is this necessary? Or will default to inherited class?
    def __eq__(self,other):
        return str(self) == str(other)

现在 total_ordering 将看到 __lt____le____ge__ 等于“原始”object 值并根据需要覆盖它们。


说了这么多,我认为这是对继承的不良使用。您至少违反了 Liskov substitution,因为 strSortableStr 之间的混合比较会产生违反直觉的结果。

我更一般的建议是支持 composition over inheritance,而不是定义一个“是”特殊字符串的东西,考虑定义一个“包含”字符串并具有特殊行为的类型。

@total_ordering
class SortableStr:

    def __init__(self,value):
        self.value = value

    def __gt__(self,other):
        return self.value + other.value > other.value + self.value

    def __eq__(self,other):
        return self.value == other.value

那里,不需要魔法。现在 SortableStr("99") 是一个有效的对象,它不是一个字符串但表现出你想要的行为。

,

不确定这是否正确,但看了 functools.total_ordering 的文档,这对我来说很突出:

给定一个定义一个或多个丰富的比较排序方法的类, 这个类装饰器提供其余的

强调我的。您的类从 __lt__ 继承 str,因此它不会被 total_ordering 重新实现,因为它没有丢失。这是我最好的猜测。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...