Python中的sorted()有啥神奇的方法吗?

Posted

技术标签:

【中文标题】Python中的sorted()有啥神奇的方法吗?【英文标题】:is there a magic method for sorted() in Python?Python中的sorted()有什么神奇的方法吗? 【发布时间】:2018-07-29 19:26:11 【问题描述】:

我知道 python 中有一些魔术方法可以被类覆盖,以控制某些内置函数处理这些类成员的方式。例如,len()str() 的行为可以通过魔术方法 __len__()__str__() 覆盖:

class EmptySet(object):
    def __len__(self):
        return 0

    def __str__(self):
        return '[]'

>>> e = EmptySet()
>>> str(e)
[]

>>> len(e)
0

还有__cmp__()__ge__()__le__() 等方法来控制如何比较这些对象以及如何按list.sort() 对它们的列表进行排序。我的问题不是关于自定义列表中对象的排序,而是关于对对象本身进行排序。假设集合不为空,我想使用sorted() 对其进行排序:

class SetOfTwo(object):
    def __init__(self, a , b):
        el_0 = a
        el_1 = b

    def __len__(self):
        return 2

    def __str__(self):
        return '[, ]'.format(el_0, el_1)

如果元素不按顺序排列,我可以实现让sorted() 翻转元素吗?我正在描绘以下行为:

>>> s = SetOfTwo(2, 1)
>>> str(s)
[2, 1]

>>> t = sorted(s)
>>> str(t)
[1, 2]

>>> type(t)
>>> SetOfTwo

【问题讨论】:

集合本质上是无序的。见this question。至于您的实际问题(除了命名法),只需在类上定义您自己的排序方法并使用它。或者传入一个 lamda。 @JaredSmith 我不认为这个问题真的是关于集合的。 你可以试试 OrderedSet 我很抱歉在我的玩具示例中使用了固有的无序集合。希望关于 python 中是否存在 sorted() 的魔术方法的问题仍然很清楚。 Python 的 sorted() 函数接受一个可迭代对象并返回一个列表。因此,如果您的集合支持迭代,则可以将其传递给sorted()。元素的排序基于它们的自然排序或传递给sorted() 的键函数。你会得到一个列表,而不是你的自定义类型。 【参考方案1】:

您绝对应该阅读the official documentation,了解如何模拟容器类型。基本上,一个应该作为容器(列表、字典等)工作的类需要实现方法来设置或获取成员 __getitem__()__setitem__() 并迭代项目 __iter__() 并获取项目数量 - 方法 @987654325 @。 这是最低限度。但您也可以添加删除项目和其他操作的功能。

sorted() 内置函数的行为是遍历容器的元素并使用您提到的方法比较它们 __cmp__(), __ge__(), __le__() 应该为项目而不是您已经知道的容器定义。然后创建一个新的list 实例,其中包含已排序的项目,并返回这个新实例。然后你可以将它传递给你的自定义容器的构造函数,或者你可以使用一个自定义函数 wriap sorted(),它将返回所需的类实例。

【讨论】:

模拟容器类型的链接很有帮助。基本上,如果sorted() 有一个神奇的方法,它会在官方文档中显示为__sorted__()。我几乎问了这个关于reversed() 的问题,而该页面确实列出了__reversed__() 魔术方法。所以有一种神奇的方法可以实现你自己的reversed() 行为而不是sorted()。在您阅读文档的正确部分之前,这并不明显。 这是因为反转集合并不关心项目的值,而是根据这些值对其进行排序。 在我看来唯一重要的区别是 reversed() 在容器中寻找 __reversed__() 方法,而 sorted() 不寻找 __sorted__() 方法。跨度> 这是技术上的差异——你是对的。但是我在上一条评论中解释了 Python 开发人员以这种方式实现它的原因。可以想象一个类以某种方式对项目进行排序而不进行比较,但它不能称为“排序”。 还有其他原因要覆盖排序。例如,我可能想添加一个 issorted 标志,该标志跳过排序算法并返回对象的副本,如果已知对象之前已排序。【参考方案2】:

正如一些人在 cmets 中所说,集合是无序的,但我认为您的问题并不是关于集合。

Python 使用您提到的数据模型方法 gelecmp 来确定类在 sorted() 被呼吁它。你可以在这里看到我是如何尝试调用它的,但是 Python 反对并要求我实现

>>> class a(object):
...   pass
...
>>> b = a()
>>> c = a()
>>> d = [b, c]
>>> sorted(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'a' and 'a'

希望这会有所帮助。此外,正如其他人所说,在 collections.abc 中对某些内容进行子类化是个好主意。我会阅读 Effective python 中的第 28 条,其中谈到了这一点以获得一个好主意。

【讨论】:

【参考方案3】:

len()str() 是接受对象作为参数并返回整数(分别为字符串)的函数。该对象可以通过__len__()__str__() 魔术方法个性化计算len 或生成字符串的方式。

同样,sorted() 是一个函数,它接受对象列表(或任何可迭代对象)并返回已排序对象的列表。对象可以通过__lt__() 魔术方法个性化它们的比较方式。

当我们将 `sorted(my_list) 视为“对列表进行排序”而不是“对列表的元素进行排序”的函数时,会出现一些混淆。

您不想对对象进行排序(即制作对象的有序列表),而只想对在其内部表示中的某些数据进行排序。因此,您需要在对象上使用一个实例方法来更新该内部表示。你可以随意命名它,.sort() 如果你愿意,但你必须在你的一个对象上调用它,它不会参与比较对象。

【讨论】:

你能解释一下如何在不比较的情况下对任何东西进行排序吗? @ElmoVanKielmo 也许我的意思可以用一个例子更清楚地解释:在问题中,OP想要在SetOfTwo对象中对el_0el_1属性进行排序(他表示作为“对对象本身进行排序”)。他可以有一个方法来做到这一点,他当然需要比较el_0el_1。但所有这些都与一堆SetOfTwo 对象的排序(如sorted())完全无关,其中涉及将SetOfTwo 与另一个SetOfTwo 进行比较。对象之间的这种(也是必要的)比较可以通过对象的__lt__ 方法来完成。

以上是关于Python中的sorted()有啥神奇的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

有啥类似于 Python 在 c++ 中的索引吗?

HTML5中的Canvas到底有啥神奇的力量?

Python通向百万程序员的秘籍!这些技巧你知道吗?99%的不知道!

Python中的“字符串”和“字符串”有啥区别吗? [复制]

计算欧几里得距离的python方法的精度有啥不同吗?

Windows 中的 python 2 和 python 3 之间的命名管道的工作方式有啥不同吗?