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


以上


  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
from django.contrib import admin from django.urls import reverse from django.utils.html import format_html from django.shortcuts import redirect from .models import Drug @admin.register(Drug) class DrugAdmin(admin.ModelAdmin): change_list_template = 'admin/drug/change_list.html' def get_urls(self): urls = super().get_urls() custom_urls = [ path('import-csv/', self.import_csv), ] return custom_urls + urls def import_csv(self, request): if request.method == 'POST': # TODO: import CSV data self.message_user(request, 'CSV data imported successfully') return redirect('..') return render(request, 'admin/drug/import_csv.html') def changelist_view(self, request, extra_context=None): if not request.GET.get('ordering'): # set default ordering request.GET = request.GET.copy() request.GET['ordering'] = 'name' return super().changelist_view(request, extra_context=extra_context) def interaction_display(self, obj): return format_html('<pre>{}</pre>', obj.interaction) interaction_display.short_description = 'Interaction' def get_actions(self, request): actions = super().get_actions(request) del actions['delete_selected'] return actions def delete_model(self, request, obj): # TODO: delete model pass def delete_selected(self, request, queryset): # TODO: delete selected models pass def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.order_by('name') return qs def add_view(self, request, form_url='', extra_context=None): self.change_list_template = None return super().add_view(request, form_url=form_url, extra_context=extra_context) def change_view(self, request, object_id, form_url='', extra_context=None): self.change_list_template = None return super().change_view(request, object_id, form_url=form_url, extra_context=extra_context) def delete_view(self, request, object_id, extra_context=None): self.change_list_template = None return super().delete_view(request, object_id, extra_context=extra_context) 怎么重写ModelAdmin中的特性
最新发布
05-18

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值