当比较对象的元组时,显然调用对象的__eq__
方法,然后调用比较方法:
import timeit
setup = """
import random
import string
import operator
random.seed('slartibartfast')
d={}
class A(object):
eq_calls = 0
cmp_calls = 0
def __init__(self):
self.s = ''.join(random.choice(string.ascii_uppercase) for _ in
range(16))
def __hash__(self): return hash(self.s)
def __eq__(self, other):
self.__class__.eq_calls += 1
return self.s == other.s
def __ne__(self, other): return self.s != other.s
def __cmp__(self, other):
self.__class__.cmp_calls += 1
return cmp(self.s ,other.s)
for i in range(1000): d[A()] = 0"""
print min(timeit.Timer("""
for k,v in sorted(d.iteritems()): pass
print A.eq_calls
print A.cmp_calls""", setup=setup).repeat(1, 1))
print min(timeit.Timer("""
for k,v in sorted(d.iteritems(),key=operator.itemgetter(0)): pass
print A.eq_calls
print A.cmp_calls""", setup=setup).repeat(1, 1))
指纹:
8605
8605
0.0172435735131
0
8605
0.0103719966418
因此,在第二种情况下,我们直接比较键(即 A 实例),__eq__
不被调用,而在第一种情况下,显然元组的第一个 elle 是通过相等然后通过 cmp 进行比较的。但是为什么不直接通过 cmp 比较它们?我真的不太明白的是缺少 cmp 或键参数的默认sorted
行为。
元组比较就是这样实现的: 元组比较
它搜索项目不同的第一个索引,然后进行比较。这就是为什么您会看到一个__eq__
,然后是一个__cmp__
呼叫。此外,如果您没有为 A 实现 __eq__
运算符,您将看到 __cmp__
被调用两次,一次用于相等,一次用于比较。
例如
print min(timeit.Timer("""
l =list()
for i in range(5):
l.append((A(),A(),A()))
l[-1][0].s='foo'
l[-1][1].s='foo2'
for _ in sorted(l): pass
print A.eq_calls
print A.cmp_calls""", setup=setup).repeat(1, 1))
分别打印出 24 和 8 个调用(确切的数字显然取决于随机种子,但在这种情况下,它们的比率始终为 3)