functools total_ordering 似乎对继承的 class 没有任何作用
functools total_ordering doesn't appear to do anything with inherited class
我正在尝试以一种使用特殊比较的方式对字符串列表进行排序。我正在尝试使用 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
不确定这是否正确,但浏览了 functools.total_ordering
的文档,这对我来说很突出:
Given a class defining one or more rich comparison ordering methods,
this class decorator supplies the rest.
强调我的。您的 class 从 str
继承了 __lt__
,因此它不会被 total_ordering
重新实现,因为它没有丢失。这是我最好的猜测。
你是对的,内置的 str
比较运算符正在干扰你的代码。来自 the docs
Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest.
所以它只提供那些还没有定义的。在您的情况下,total_ordering
.
检测不到其中一些是在父 class 中定义的事实
现在,我们可以深入研究 source code 并找到确切的检查方法
roots = {op for op in _convert if getattr(cls, op, None) is not getattr(object, op, 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,因为 str
和 SortableStr
之间的混合比较将产生违反直觉的结果。
我更一般的建议是支持 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
,但我不确定它是否正确填写了未定义的比较。
我定义的两个(> 和 ==)按预期工作,但 < 没有。特别是,我打印了所有三个,我得到了 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
不确定这是否正确,但浏览了 functools.total_ordering
的文档,这对我来说很突出:
Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest.
强调我的。您的 class 从 str
继承了 __lt__
,因此它不会被 total_ordering
重新实现,因为它没有丢失。这是我最好的猜测。
你是对的,内置的 str
比较运算符正在干扰你的代码。来自 the docs
Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest.
所以它只提供那些还没有定义的。在您的情况下,total_ordering
.
现在,我们可以深入研究 source code 并找到确切的检查方法
roots = {op for op in _convert if getattr(cls, op, None) is not getattr(object, op, 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,因为 str
和 SortableStr
之间的混合比较将产生违反直觉的结果。
我更一般的建议是支持 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")
是一个有效的对象,它 不是 字符串但表现出您想要的行为。