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 使用您提到的数据模型方法 ge、le 和 cmp 来确定类在 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_0
和el_1
属性进行排序(他表示作为“对对象本身进行排序”)。他可以有一个方法来做到这一点,他当然需要比较el_0
和el_1
。但所有这些都与一堆SetOfTwo
对象的排序(如sorted()
)完全无关,其中涉及将SetOfTwo
与另一个SetOfTwo
进行比较。对象之间的这种(也是必要的)比较可以通过对象的__lt__
方法来完成。以上是关于Python中的sorted()有啥神奇的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章
Python通向百万程序员的秘籍!这些技巧你知道吗?99%的不知道!