[Python进阶] 定制类:比较篇

4.10.3 比较篇

在Python中,我们经常会比较两个数据结构是否相等,或者比较大小。对于Python内置的数据结构,这些数据结构的比较都是有着良好定义的。
用户自己写的数据结构,有时候也需要进行相互比较。而比较的逻辑实现就是通过魔术方法来完成的。
在Python中,如果自定义的类中没有写比较逻辑的时候,默认用is进行比较。

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date


x = Date(2022, 2, 22)
y = Date(2022, 2, 22)
ic(x == y)  # 相当于:ic(x is y)

16:34:19|> x == y: False

4.10.3.1 eqne

判断两个对象是否相等时“==”会调用这个方法。

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __eq__(self, other):
        return self.year == other.year and self.month == other.month and self.date == other.date


x = Date(2022, 2, 22)
y = Date(2022, 2, 22)
ic(x == y)

16:39:20|> x == y: True

Python如果发现在自建类中没有定义不等于方法,那么会将等于方法取反作为不等于的返回值。

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __eq__(self, other):
        print('__eq__')
        return self.year == other.year and self.month == other.month and self.date == other.date


x = Date(2022, 2, 22)
y = Date(2022, 11, 22)
ic(x != y)

eq
16:50:37|> x != y: True

如果定义了不等于方法,那么在判断不等于时,只会调用不等于方法。

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __eq__(self, other):
        print('__eq__')
        return self.year == other.year and self.month == other.month and self.date == other.date

    def __ne__(self, other):
        print('__ne__')
        return self.year != other.year or self.month != other.month or self.date != other.date


x = Date(2022, 2, 22)
y = Date(2022, 11, 22)
ic(x != y)

ne
16:53:01|> x != y: True

一般情况下,我们只需要在自建类中定义一个__eq__方法即可。
在__eq__和__ne__方法中,都需要提供另一个需要比较的对象:other,这是因为这2个方法都是用来进行二元比较的。
但我们在判断x是否和y相等时:x == y,其实是在判断:x.eq(y)。y会被当成other传入到x的__eq__方法中。

4.10.3.2 gtlt

等于、不等于是有默认实现的,如果不定义,默认会用is进行判断。但是大于、小于如果没有定义,直接比较是会报错的。

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __eq__(self, other):
        print('__eq__')
        return self.year == other.year and self.month == other.month and self.date == other.date

    def __ne__(self, other):
        print('__ne__')
        return self.year != other.year or self.month != other.month or self.date != other.date

    def __gt__(self, other):
        if self.year > other.year:
            return True
        if self.year == other.year:
            if self.month > other.month:
                return True
            if self.month == other.month:
                return self.date > other.date


x = Date(2023, 2, 22)
y = Date(2022, 11, 22)
ic(x > y)

17:08:33|> x > y: True

这里虽然我们只定义了大于方法,但是我们此时可以直接使用小于号,因为Python默认大于号和小于号是一对。当不做特殊说明的时候,x > y就意味着y < x。如果判断x < y,在x中没有找到小于方法,就会去找y中的大于方法,这样也能进行判断。

4.10.3.3 gele

Python中不会对小于等于、大于等于进行推测,也就是,大于等于是一个整体,而非大于或等于。在没有定义大于等于时,如果直接用大于等于判断时会报错,而非调用大于、等于后进行or逻辑运算。

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __ge__(self, other):
        print('__ge__')
        if self.year > other.year:
            return True
        if self.year == other.year:
            if self.month > other.month:
                return True
            if self.month == other.month:
                return self.date >= other.date
    def __le__(self, other):
        print('__le__')
        if self.year < other.year:
            return True
        if self.year == other.year:
            if self.month < other.month:
                return True
            if self.month == other.month:
                return self.date <= other.date


x = Date(2023, 2, 22)
y = Date(2022, 11, 22)
ic(x >= y)

ge
17:16:13|> x >= y: True

4.10.3.4 hash

在Python中,每个自定义类都会默认有个__hash__方法进行绑定。如果定义了__eq__方法,那么这个默认的__hash__方法就会被删除掉。这个自定义类就会变得不可hash了。
原因是,在Python中,如果两个对象相等,那么这两个对象的hash值也必须相等。于是,我们必须在定义__eq__方法后再自行定义__hash__方法。
__hash__方法的要求有:
1、必须返回一个整数
2、对于两个相等的对象,hash值也必须相同
最好使用Python中自带的hash函数进行求值,如下:

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __eq__(self, other):
        return self.year == other.year and self.month == other.month and self.date == other.date

    def __hash__(self):
        return hash((self.year, self.month, self.date))


x = Date(2023, 2, 22)
y = Date(2023, 2, 22)
dct = {x: 999, y: 999}
ic(dct)

17:27:37|> dct: {<main.Date object at 0x000001A6C49EBEE0>: 999}

4.10.3.5 bool

对于我们所有的自定义对象,如果直接作为if语句的判断条件,都是返回True。如果想改变自定义对象在做boolean运算时候的结果,那么可以使用__bool__这个魔法方法。

from icecream import ic


class Date:
    def __init__(self, year, month, date):
        self.year = year
        self.month = month
        self.date = date

    def __bool__(self):
        print('bool')
        return False



x = Date(2023, 2, 22)
ic(bool(x))
if not x:
    ic(x)

bool
bool
ic| bool(x): False
ic| x: <main.Date object at 0x0000026ECCC633A0>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0思必得0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值