【问题标题】:Sorting class instances in python在python中对类实例进行排序
【发布时间】:2011-12-27 23:41:16
【问题描述】:

python 2.7 使用什么来对 vanilla 类实例进行排序?我对默认排序行为感兴趣。

假设我有课

class S():
    pass

然后我可以创建几个实例,并对它们进行排序:

a = S(); b = S(); c = S()
l = [(a,'a'), (b,'b') ,(c, 'c')]
sorted(l)

这将打印一些对象的排序。现在我有一个两部分的问题:

  • python 是否使用对象的__hash__(),以及它们的id()
  • 是否可以覆盖__hash__() 来影响排序行为?

【问题讨论】:

  • 这是 Python 2 还是 3?我的冲动是“覆盖 ltgt 等”,但我对 Python 2 的“经典”类的理解参差不齐,所以不知道这是否合适。您可能还会发现此链接有帮助:wiki.python.org/moin/HowTo/Sorting

标签: python sorting


【解决方案1】:

Python 3 的内置排序在您的类中使用了__lt__ 方法。

丰富的比较方法在 Python 中是特殊的,因为如果没有定义 __lt__,它们可以返回一个特殊的 NotImplemented 类型 - 请查看此页面上的文档:http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy

由于NotImplemented 的真值是True,任何得到NotImplemented 的布尔比较都将继续进行,就好像第一个元素实际上小于第二个元素一样,这将导致排序使列表保持不变照原样订购。

看看交互式外壳。您可以看到真值如何在排序中使用,并且 Python 认为两个对象都小于彼此:

>>> class S():
...     pass
...
>>> a = S()
>>> b = S()
>>> a.__lt__( b )
NotImplemented
>>> if a.__lt__( b ):
...     print( "derp!" )
...
derp
>>> if b.__lt__(a):
...     print( "derp" )
...
derp

这里还有一些参考资料:

编辑: 在查看 Python 2.7 之后,看起来对象的 ID 用于排序,并且 __lt__ 方法在像您的示例这样的简单类上未定义。如有任何混淆,请见谅。

【讨论】:

  • 但是,实例并没有按顺序排列!在我的示例中,我得到[b, c, a]
  • 是的,我意识到我错过了 2.7 角度(我一直在使用 3.2)。在交互式 shell 中进行了一些类似的调查后,看起来确实使用了类的 ID。但是,您可以看到不应依赖此行为,尤其是在您要在 Python 版本之间迁移时。
  • 依赖默认排序首先让我遇到了麻烦。如果 python 对对象的 id 进行二次排序,使用 (value, object) 元组对列表进行排序是非常危险的!我不知道这些东西在 python 版本之间发生了变化,我确实在使用 2.x。
【解决方案2】:

Python 的排序算法专门使用“小于”测试来比较项目,可以使用 __cmp__() 特殊方法(现已弃用)或类上的 __lt__() 来实现。

在没有任何关于如何比较两个对象的具体说明的情况下,id() 用于相同类型的对象(而不是哈希),就像您的情况一样。

【讨论】:

    【解决方案3】:

    我在自己的机器(python 2.7)上尝试了您的示例:

    >>> sorted(l)
    [(<__main__.S instance at 0x107142a70>, 'a'), (<__main__.S instance at 0x107142ab8>, 'b'), (<__main__.S instance at 0x107142b00>, 'c')]
    

    注意排序后的列表是按照id()的顺序排列的:

    >>> id(a)
    4413729392
    >>> id(b)
    4413729464
    >>> id(c)
    4413729536
    

    如果我生成我得到的哈希:

    >>> hash(a)
    275858087
    >>> hash(b)
    -9223372036578917717
    >>> hash(c)
    275858096
    

    这表明排序不是基于哈希的

    请参阅 derekerdmann 的回答,了解有关如何让 python 以您想要的方式对您的类进行排序的信息。

    编辑:顺便说一下,如果你把列表中的项目倒过来然后排序,你会得到:

    >>> l = [(c,'c'), (b, 'b'), (a, 'a')]
    >>> sorted(l)
    [(<__main__.S instance at 0x107142a70>, 'a'), (<__main__.S instance at 0x107142ab8>, 'b'), (<__main__.S instance at 0x107142b00>, 'c')]
    

    再一次,它按id() 的顺序排序。

    【讨论】:

    • 所以它们并没有像 derek 建议的那样按顺序排列。实际上,它说here __hash__ 默认返回id(),您如何解释差异?
    猜你喜欢
    • 2011-04-29
    • 2020-01-21
    • 2021-10-29
    • 2018-02-24
    • 2022-01-25
    • 2012-10-12
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    相关资源
    最近更新 更多