啊,最近被问到重载操作符,想要比较两个对象大小,之前理解不深刻呢,对__cmp__()和__gt__()和__lt__(),有了点疑问:有了前者,还要后面两个干嘛?
1、
先做个实验__cmp__() 和 __gt__() 冲不冲突
class Door(object):
def __init__(self):
self.value = 0
def __cmp__(self, other):
print '====my cmp===='
if self.value > other.value:
return 1
if self.value < other.value:
return -1
return 0
def __gt__(self, other):
print '====my gt===='
return self.value > other.value
def __lt__(self, other):
print '====my lt===='
return self.value < other.value
a = Door()
b = Door()
a.value = 20
b.value = 20
print a == b
print a > b
print a < b
结果:
====my cmp====
True
====my gt====
False
====my lt====
Fals
如果不带__gt__()和__lt__(),则
class Door(object):
def __init__(self):
self.value = 0
def __cmp__(self, other):
print '====my cmp===='
if self.value > other.value:
return 1
if self.value < other.value:
return -1
return 0
a = Door()
b = Door()
a.value = 20
b.value = 20
print a == b
print a > b
print a < b
结果:
====my cmp====
True
====my cmp====
False
====my cmp====
False
def __cmp__(self, other):
assert isinstance(other, A) # assumption for this example return
return cmp((self.name, self.age, self.other), (other.name, other.age, other.other))
def total_ordering(cls):
"""Class decorator that fills in missing ordering methods"""
convert = {
'__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
('__le__', lambda self, other: self < other or self == other),
('__ge__', lambda self, other: not self < other)],
'__le__': [('__ge__', lambda self, other: not self <= other or self == other),
('__lt__', lambda self, other: self <= other and not self == other),
('__gt__', lambda self, other: not self <= other)],
'__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
('__ge__', lambda self, other: self > other or self == other),
('__le__', lambda self, other: not self > other)],
'__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
('__gt__', lambda self, other: self >= other and not self == other),
('__lt__', lambda self, other: not self >= other)]
}
roots = set(dir(cls)) & set(convert)
if not roots:
raise ValueError('must define at least one ordering operation: < > <= >=')
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
for opname, opfunc in convert[root]:
if opname not in roots:
opfunc.__name__ = opname
opfunc.__doc__ = getattr(int, opname).__doc__
setattr(cls, opname, opfunc)
return cls
'__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
('__le__', lambda self, other: self < other or self == other),
('__ge__', lambda self, other: not self < other)],
如果我们不自己实现__eq__()的话,self == other这句话不会报错,但是==这玩意,我一直以为是id(a) == id(b),两个实例不同id,所以不相等,从结果上来看确实如此,但其实很打脸,==并不是用id()去判断,我们看看help(“==“)能发现这么一句:
The operators "<", ">", "==", ">=", "<=", and "!=" compare the values
of two objects. The objects need not have the same type. If both are
numbers, they are converted to a common type. Otherwise, objects of
different types *always* compare unequal, and are ordered consistently
but arbitrarily. You can control comparison behavior of objects of
non-built-in types by defining a "__cmp__" method or rich comparison
methods like "__gt__", described in section Special method names.
意思是,这两个比较的对象不需要相同type。如果都是数值,则会转成一个共同的type去比较(float 1.0 == int 1)。除此之外的情况都是不相同的。
用functools.total_ordering来搞一波示例代码
from functools import total_ordering
@total_ordering
class Door(object):
def __init__(self):
self.value = 0
self.first_name = ''
self.last_name = ''
def __eq__(self, other):
print '=== my eq==='
return (self.first_name, self.last_name) == (other.first_name, other.last_name)
def __gt__(self, other):
print '=== my total_ordering==='
return (self.first_name, self.last_name) > (other.first_name, other.last_name)
a = Door()
b = Door()
a.first_name = 'ouyang'
a.last_name = 'guoge'
b.first_name = 'aaaa'
b.last_name = 'bbbb'
print a == b
print a > b
print a < b
print a <= b
print a >= b
结果:
=== my eq===
False
=== my total_ordering===
True
=== my total_ordering===
False
=== my total_ordering===
False
=== my total_ordering===
True