python cmp函数未定义_错误笔记:覆写Python中的__cmp__

最近,在做一个简单的排名函数时,我不小心踩到了Python语言的一个坑里。我花了很多时间才找出具体的原因。这是值得记录的。

具体功能要求如下:对500人进行前500名排名,每个人的分数只增加而不减少,当得分相同时,比较列表上的时间戳,得分第一的用户排名。

这个列表之前已经做过很多,一般来说,会先抽象一个TopItem对象,保存用户的点,以及名称、UUID和其他相关信息。然后,使用列表对象top保存500个有序元素,使用dict对象缓存保存从uuid到topitem的映射。由于python对象的reference属性,添加一个额外的dict对象不会增加topitem对象的副本,但都指向同一topitem对象,所以不会有大的内存问题。

因此,整个数据结构可能显示在下面的伪代码中:

class TopItem(object):

def __init__(self, uuid, name, score, t):

self.uuid = uuid

self.name = name

self.score = score

self.t = t

class TopStub(object):

def __init__(self, tops):

self.tops = copy.deepcopy(tops)

self.cache = {}

for item in self.tops:

if item.uuid not in self.cache:

self.cache[item.uuid] = item

当玩家播放列表时,首先根据uuid从缓存中查找topitem对象,如果找不到该对象,则直接根据分数以二进制搜索的方式插入;如果找到对象topitem,则更新其分数、名称等信息,然后将其从top列表中删除,然后插入。在二进制搜索中(因为分数只增加或不减少,所以这里有优化)。空间)。

这个排名,计划需要很多能够处理相同的分数(比较时间戳),所以我自然会考虑重写topitem的cmp_u函数,这样你就可以直接使用<>比较topitem,写完代码后就不漂亮了。然后编写以下CMP_uuu函数:

class TopItem(object):

# see above

def __cmp__(self, other):

if self.score != other.score:

return cmp(self.score, other.score)

else:

return cmp(other.t, self.t)

class TopStub(object):

# see above

在测试期间,我做了一个测试功能。我一次随机插入n个用户数据的随机分数,QA通过了测试。没问题。

直到它上线。

大约两个小时后,我发现排名完全混乱,出现了“分阶段下降”的现象,用户信息在排名中反复出现。

经过彻底的调查,发现问题出在“CMP”和“清单”上。远程的从_cmp_u的定义可以看出,如果两个顶项的得分和t相等,那么我们将判断两个顶项是相等的。名单。删除(item)删除与item相等的第一个元素。“CMP”的改写对平等判断有明显影响。也就是说,如果两个顶级项的整数和时间戳相等,那么列表中的顶级项可能是错误的。删除(项)步骤将发生,以便在排名中同时出现多个相同的用户信息。

写一段代码来验证:

def test():

t1 = TopItem('uuid1', 'aaa', 80, 10)

t2 = TopItem('uuid2', 'bbb', 90, 11)

t3 = TopItem('uuid3', 'ccc', 90, 11)

t4 = TopItem('uuid4', 'ddd', 100, 11)

tops = [t4, t3, t2, t1]

print 'tops:', tops

tops.remove(t2)

print 'after remove t2:bbb'

print 'tops:', tops

output:

tops: [(ddd,100,11), (ccc,90,11), (bbb,90,11), (aaa,80,10)]

after remove t2:bbb

tops: [(ddd,100,11), (bbb,90,11), (aaa,80,10)]

果然没错,我们想删除t2(bbb),但是却把和t2“相等”的t3(ccc)删除了。

那么接下来的修正也很简单:

def __cmp__(self, other):

if self.score != other.score:

return cmp(self.score, other.score)

elif self.t != other.t:

return cmp(other.t, self.t)

else:

return cmp(id(self), id(other))

run test again and output:

tops: [(ddd,100,11), (ccc,90,11), (bbb,90,11), (aaa,80,10)]

after remove t2:bbb

tops: [(ddd,100,11), (ccc,90,11), (aaa,80,10)]

总结

记得之前看一些Python的文章时,有人说过,对于双下划线开头的这些函数,除非你确切知道自己在干什么,否则不要覆写这些函数。当时我还不以为然:既然Python提供了这么多有意思的钩子,干嘛不用呢?现在我才明白。

另外,覆写cmp函数时,在最后一个else分支中,比较id是比较稳妥的做法。

最后,Python3已经废弃掉cmp函数,改为提供eq, lt等多个函数,进一步降低使用者犯错的几率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值