QQ登录

只需一步,快速开始

开启左侧

Python学习第六十四天—类和对象13-代偿

[复制链接]
15271953841 发表于 2024-3-28 08:19:48 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
代偿
一、__contains__(self,item)实现成员关系的检测,对应的运算符是in,not in
Python 3.12.1 (tags/v3.12.1:2305ca5, Dec  7 2023, 22:03:25) [MSC v.1937 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
>>>class C:
    def __init__(self,data):
        self.data = data
    def __contains__(self,item):
        print("嗨~")           (看到“嗨~”,知道这个魔法方法成功拦截了)
        return item in self.data

   
>>>c = C([1,2,3,4,5])
>>>3 in c            (3就是对应上面的contains魔法方法里面的item这个参数,传到这个参数里面去,然后如果它(item)是in self.data的话,也就是3如果在[1,2,3,4,5]这个列表里的话,它就返回True.)
嗨~            
True
>>>6 in c
嗨~
False
代偿:Python魔法方法其实也有很多类似代偿的实现,比如上一节讲到的迭代,假如你没有定义iter和next魔法方法,那么当你把对象放到迭代工具中,比如说,放到for循环语句中的时候,Python在找不到iter和next魔法方法的情况下,就会尝试去查找这个getitem的魔法方法,这个上一节课也是举过例子了。
那么这个contains魔法方法事实上也是有代偿的。
如果说,我们没有实现这个contains,但是又使用了in和not in进行程序关系判断,Python就会尝试去查找iter哈next魔法方法,这你受得了吗?
>>>class C:
    def __init__(self,data):
        self.data = data
    def __iter__(self):
        print("Iter",end = "->")
        self.i = 0
        return self
    def __next__(self):
        print("Next",end = "->")
        if self.i == len(self.data):
            raise StopIteration
        item = self.data[self.i]
        self.i += 1
        return item

   
>>>c = C([1,2,3,4,5])
>>>3 in c               (我们现在没有实现contains的魔法方法,它会使用iter和next这两种魔法方法进行代偿,它首先调用iter魔法方法,把它变成一个迭代器,然后挨个拿出data里面的数据,直到找到的时候,它就会返回True)
Iter->Next->Next->Next->True
>>>6 in c
Iter->Next->Next->Next->Next->Next->Next->False         (找了一遍,全没找到,只到这个迭代器抛出StopIteration还是没有找到,那么它就返回False,就是一个非常智能的操作。)

如果连iter和next魔法方法,这里面都没有,那么Python还会尝试再找找有没有getitem魔法方法的实现,如果有的话,也能凑合着用。
>>>class C:
    def __init__(self,data):
        self.data = data
    def __getitem__(self,index):
        print("Getitem",end = "->")
        return self.data[index]

   
>>>c = C([1,2,3,4,5])
>>>3 in c
Getitem->Getitem->Getitem->True
>>>6 in c
Getitem->Getitem->Getitem->Getitem->Getitem->Getitem->False

二、布尔测试(参考复习第6课时)
如果遇到布尔函数bool(),Python首先会去寻找__bool__()的魔法方法,举例:
>>>class D:
    def __bool__(self):
        print("Bool")
        return True

   
>>>d = D()
>>>bool(d)
Bool
True
>>>class D:
    def __init__(self,data):
        self.data = data
    def __len__(self):
        print("Len")
        return len(self.data)

   
>>>d = D("FishC")            (实例化的时候传入一个字符串,那么这个时data,self.data里面就是”FishC”这个字符串,return的是什么,return是self.data的长度,非零,给到的是True)
>>>bool(d)
Len
True
>>>d = D("")                 (这里给到一个空字符串,空字符串的长度为0)
>>>bool(d)
Len
False

三、跟比较运算相关的魔法方法
<, 小于号(less than)                 __lt__(self,other)
<=,小于等于号(less than or equal)      __le__(self,other)
>, 大于号(greater than)            __gt__(self,other)
>=,大于等于号(greater than or equal)   __ge__(self,other)
==,等于号(equal to)                  __eq__(self,other)
!=,不等于号(not equal to)             __ne__(self,other)
这些运算符触发的方法。
>>>class S(str):
    def __lt__(self,other):
        return len(self) < len(other)
    def __gt__(self,other):
        return len(self) > len(other)
    def __eq__(self,other):
        return len(self) == len(other)

   
>>>s1 = S("FishC")
>>>s2 = S("fishc")
>>>s1 < s2
False
>>>s1 > s2
False
>>>s1 == s2               (因为我们现在判断的是它们的长度,而与字符编码没有关系)
True
>>>s1 != s2             (这是怎么回事?这里我们通过__eq__()这个魔法方法实现了对等值判断的拦截,但是这并不是意味着不等值判断就会自动取这个等值判断的相反结果,如果我们执行s1 != s2这么一个判断,由于在这个类中没有定义,走的还是其父类,也就是字符串的传统比较路线,比较的还是编码值,所以同样的道理,小于等于和大于等于也是一样的,比较的是编码值的大小。)
True
>>>s1 <= s2
True
>>>s1 >= s2
False

如果我们不想让某个魔法方法生效,我们其实可以直接将它赋值为None
class S(str):
    def __lt__(self,other):
        return len(self) < len(other)
    def __gt__(self,other):
        return len(self) > len(other)
    def __eq__(self,other):
        return len(self) == len(other)
    __le__ = None
    __ge__ = None
    __ne__ = None

   
s1 = S("FishC")
s2 = S("fishc")
s1 != s2
Traceback (most recent call last):
  File "<pyshell#79>", line 1, in <module>
    s1 != s2
TypeError: 'NoneType' object is not callable  (报错,TypeError异常,这种做法也可应用前面的代偿实现)
class C:
    def __init__(self,data):
        self.data = data
    def __iter__(self):
        print("Iter",end = "->")
        self.i = 0
        return self
    def __next__(self):
        print("Next",end = "->")
        if self.i == len(self.data):
            raise StopIteration
        item = self.data[self.i]
        self.i += 1
        return item
    __contains__ = None    (我们明确把__contains__赋值为None,明确的表示说,我们不希望关系判断这种事情出现)

   
c = C([1,2,3,4,5])
3 in c
Traceback (most recent call last):
  File "<pyshell#84>", line 1, in <module>
    3 in c
TypeError: 'C' object is not a container
6 not in c
Traceback (most recent call last):
  File "<pyshell#85>", line 1, in <module>
    6 not in c
TypeError: 'C' object is not a container

客服热线
400-1234-888 周一至周日:09:00 - 21:00
公司地址:襄阳市樊城区长虹路现代城5号楼188

创客帮MAKER.BAND青少年创客创意社区是一个融教育、科技、体育资讯为一体的综合服务平台,专注于教育创新、专注于科技体育、专注于教育资讯。

Powered by Discuz! X3.4 © 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表