python 初始认识重载操作符的利器:functools.total_ordering

啊,最近被问到重载操作符,想要比较两个对象大小,之前理解不深刻呢,对__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
果然是比较的时候,优先使用__gt__(), __lt__(), __eq__(),如果找不到则使用__cmp__()。
Ps: 至于这个使用__cmp__()还是__gl__()的控制逻辑并没有找到相关文档。有大神看到这里求指导呀~

可惜python3 没有cmp()函数啦,我们要尽可能少用cmp(),否则还能来一波: 
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))

2、

当然在Stack Overflow上,找到更优解: https://stackoverflow.com/questions/1061283/lt-instead-of-cmp
最牛逼的还是得用functools.total_ordering(): 
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__(), __ge__()等等的N多函数。
不过注意观察,就用__lt__()这一个分支来说, 
'__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)。除此之外的情况都是不相同的。

3、

用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


以上


你可能感兴趣的:(Python,python,窥探源码)