Python自定义比较器是如何工作的?

Python自定义比较器是如何工作的?,python,python-2.x,Python,Python 2.x,我有以下Python指令: [(2, [3, 4, 5]), (3, [1, 0, 0, 0, 1]), (4, [-1]), (10, [1, 2, 3])] 现在我想根据dictionary值的值和对它们进行排序,因此对于第一个键,值和是3+4+5=12 我编写了以下代码来完成这项工作: def myComparator(a,b): print "Values(a,b): ",(a,b) sum_a=sum(a[1]) sum_b=sum(b[1]) pri

我有以下Python指令:

[(2, [3, 4, 5]), (3, [1, 0, 0, 0, 1]), (4, [-1]), (10, [1, 2, 3])]
现在我想根据dictionary值的值和对它们进行排序,因此对于第一个键,值和是3+4+5=12

我编写了以下代码来完成这项工作:

def myComparator(a,b):
    print "Values(a,b): ",(a,b)
    sum_a=sum(a[1])
    sum_b=sum(b[1])
    print sum_a,sum_b
    print "Comparision Returns:",cmp(sum_a,sum_b)
    return cmp(sum_a,sum_b)

items.sort(myComparator)
print items
这是我在上面运行后得到的输出:

Values(a,b):  ((3, [1, 0, 0, 0, 1]), (2, [3, 4, 5]))
2 12
Comparision Returns: -1
Values(a,b):  ((4, [-1]), (3, [1, 0, 0, 0, 1]))
-1 2
Comparision Returns: -1
Values(a,b):  ((10, [1, 2, 3]), (4, [-1]))
6 -1
Comparision Returns: 1
Values(a,b):  ((10, [1, 2, 3]), (3, [1, 0, 0, 0, 1]))
6 2
Comparision Returns: 1
Values(a,b):  ((10, [1, 2, 3]), (2, [3, 4, 5]))
6 12
Comparision Returns: -1
[(4, [-1]), (3, [1, 0, 0, 0, 1]), (10, [1, 2, 3]), (2, [3, 4, 5])]

现在我无法理解比较器是如何工作的,传递了哪两个值,以及会发生多少这样的比较?它是否在内部创建键的排序列表,以便跟踪每次比较?而且这种行为似乎非常随机。我很困惑,任何帮助都将不胜感激。

数字和所做的比较没有文档记录,事实上,它可以在不同的实现中自由更改。唯一的保证是,如果比较函数有意义,该方法将对列表进行排序

CPython使用排序列表,因此您看到的是该算法执行比较的顺序(如果我没有误认为非常短的列表,那么Timsort只使用插入排序)

Python不跟踪“键”。它只是在每次进行比较时调用比较函数。因此,可以多次调用函数
len(items)

如果要使用键,应使用
参数。事实上,你可以做到:

items.sort(key=lambda x: sum(x[1]))
这将创建键,然后使用键上的常用比较运算符进行排序。这保证只调用
key
传递的函数
len(items)


鉴于您的列表是:

[a,b,c,d]
您看到的比较顺序是:

b < a   # -1  true   --> [b, a, c, d]
c < b   # -1  true   --> [c, b, a, d]
d < c   # 1   false
d < b   # 1   false
d < a   # -1  true   --> [c, b, d, a]
b[b,a,c,d]
c[c,b,a,d]
d[c,b,d,a]

基本上,对于像[2,4,6,3,1]这样的简单列表和您提供的复杂列表,排序算法是相同的

唯一的区别是列表中元素的复杂性以及如何比较任意两个元素的比较方案(例如,您提供的
myComparator

Python排序有一个很好的描述:

首先,cmp()函数:

cmp(...)
    cmp(x, y) -> integer
    Return negative if x<y, zero if x==y, positive if x>y.
我想这正是你想要的。根据每个元组列表的
sum()
排序
mylist

比较器是如何工作的

这很好:

比较两个对象x和y,并根据结果返回一个整数。如果xy,则返回值为正

如果不调用cmp函数,您可以编写:

sum_a=sum(a[1])
sum_b=sum(b[1])
if sum_a < sum_b: 
   return -1
elif sum_a == sum_b:
   return 0
else:
   return 1
然后通过函数比较每个元组中两个列表的总和,在代码中表示sum_a和sum_b

有多少这样的比较会发生

我猜你真正想问的是:通过调用单个函数,排序是如何工作的

简单的回答是:它使用算法,调用比较函数O(n*logn)次(注意,实际调用次数是c*n*logn,其中c>0)

为了理解发生了什么,想象一下你自己正在对一系列值进行排序,比如说
v=[4,2,6,3]
。如果你系统地做这件事,你可能会这样做:

  • 从索引i=0处的第一个值开始
  • 将v[i]与v[i+1]进行比较
  • 如果v[i+1]
  • 增加i,从2重复,直到i==len(v)-2
  • 从1开始,直到不再发生交换
  • 所以你得到了,我=

    0: 2 < 4 => [2, 4, 6, 3] (swap)
    1: 6 < 4 => [2, 4, 6, 3] (no swap)
    2: 3 < 6 => [2, 4, 3, 6] (swap)
    
    0:2<4=>[2,4,6,3](互换)
    1:6<4=>[2,4,6,3](无掉期)
    2:3<6=>[2,4,3,6](互换)
    
    重新开始:

    0: 4 < 2 => [2, 4, 3, 6] (no swap)
    1: 3 < 4 => [2, 3, 4, 6] (swap)
    2: 6 < 4 => [2, 3, 4, 6] (no swap)
    
    0:4<2=>[2,4,3,6](无交换)
    1:3<4=>[2,3,4,6](互换)
    2:6<4=>[2,3,4,6](无掉期)
    
    重新开始-不会有进一步的交换,所以停止。您的列表已排序。在本例中,我们已将该列表运行了3次,共进行了3*3=9次比较

    显然,这不是很有效,
    sort()
    方法只调用您的比较器函数5次。原因是它采用了比上面解释的简单排序算法更有效的排序算法

    而且这种行为似乎非常随机

    请注意,传递给比较器函数的值序列通常没有定义。但是,sort函数在它接收的iterable的任意两个值之间进行所有必要的比较

    它是否在内部创建键的排序列表,以便跟踪每次比较


    不,它没有在内部保存密钥列表。相反,排序算法本质上是对您给出的列表进行迭代。事实上,它构建列表子集以避免进行太多的比较-排序算法的工作原理有一个很好的可视化效果

    您是在尝试对字典还是列表进行排序?字典是无序的,这意味着你不能改变它们的顺序。在您的示例中,您显示了一个元组列表。您试图排序什么?请注意,尽管Python 2支持将自定义比较函数传递给
    sort()
    ,但Python 3不支持。主要原因是使用自定义键函数要高效得多,因为每个列表项只需调用一次键函数,而每次比较都必须调用比较函数。有关详细信息,请参见Russelllo答案中的链接。这里有一个类似的问题:美丽的答案!感谢您的帮助。感谢您一步一步地通过比较器处理示例列表+1.
    0: 2 < 4 => [2, 4, 6, 3] (swap)
    1: 6 < 4 => [2, 4, 6, 3] (no swap)
    2: 3 < 6 => [2, 4, 3, 6] (swap)
    
    0: 4 < 2 => [2, 4, 3, 6] (no swap)
    1: 3 < 4 => [2, 3, 4, 6] (swap)
    2: 6 < 4 => [2, 3, 4, 6] (no swap)