鱼C论坛

 找回密码
 立即注册
查看: 1452|回复: 7

[知识点备忘] 第070讲:类和对象(XIII)

[复制链接]
发表于 2022-8-9 16:43:46 | 显示全部楼层 |阅读模式
购买主题 已有 17 人购买  本主题需向作者支付 5 鱼币 才能浏览
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2022-9-30 18:43:48 | 显示全部楼层
魔法方法__contains__()用于实现成员关系的检测,当使用in和not in进行成员关系判断时便会触发该方法;魔法方法__bool__()则与布尔测试bool()相对应。这节课主要讲的是魔法方法的代偿机制,所谓代偿,就是替代+补偿,指的是当本该发挥作用的魔法方法没有被定义时,Python便会退而求其次,寻找类似的相关方法来调用,如用__getitem__()作为__iter__()和__next__()的代偿,__iter__()和__next__()作为__contains__()的代偿,__len__()作为__bool__()的代偿等,看起来有点奇怪,其实也算是一种“不得已而为之”的折衷方案吧……
比较运算相关的魔法方法包括:__lt__()(<)、__le__()(<=)、__gt__()(>)、__ge__()(>=)、__eq__()(==)、__ne__()(!=)。通过重写这些魔法方法,可以实现对两个字符串长度而非编码值的快速比较。
最后,当我们不想让某个魔法方法生效时,只需将其赋值为None,此时若该魔法方法被触发,程序便会报错。通过这种手段,我们就可以阻止某些不希望的情形(如成员关系判断等)出现,抑制了Python可能引发误会的代偿机制,进而避免了难以调试的bug,真是未雨绸缪的好主意啊!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 2 反对 0

使用道具 举报

发表于 2022-12-7 23:02:22 | 显示全部楼层
Learning....
Support 小古比鱼


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-1-7 15:27:43 | 显示全部楼层
本帖最后由 Ensoleile 于 2023-1-13 00:22 编辑

代偿
  1. #__contains__()主要用于实现成员关系的检测,对应in、not in
  2. class C:
  3.     def __init__(self, data):
  4.         self.data = data
  5.     def __contains__(self, item):
  6.         print('hello')
  7.         return item in self.data

  8. c =C([1, 2, 3, 4, 5])
  9. print(3 in c)
  10. #hello
  11. #True
  12. #[1, 2, 3, 4, 5]对应self.data,3对应__contains__()魔法方法中的item

  13. #__contains__()代偿 P189 -> __iter__() + __next__() -> __getitem__()
  14. class C:
  15.     def __init__(self, data):
  16.         self.data = data
  17.     def __iter__(self):
  18.         print('iter', end='->')
  19.         self.i = 0
  20.         return self
  21.     def __next__(self):
  22.         print('next', end='->')
  23.         if self.i == len(self.data):
  24.             raise StopIteration
  25.         item = self.data[self.i]
  26.         self.i += 1
  27.         return item

  28. c =C([1, 2, 3, 4, 5])
  29. print(3 in c)
  30. #iter->next->next->next->True
  31. #首先调用iter将列表变为迭代器,再按个使用next拿出数据比较,直到找到的时候返回True
  32. print(6 in c)
  33. #iter->next->next->next->next->next->next->False

  34. class C:
  35.     def __init__(self, data):
  36.         self.data = data
  37.     def __getitem__(self, item):# item对应的不是3 in c中的3,而是迭代时的0,1,2....
  38.         print('getitem', end='->')
  39.         return self.data[item]

  40. c = C([1, 2, 3, 4, 5])
  41. print(3 in c)
  42. # getitem->getitem->getitem->True
  43. print(6 in c)
  44. # getitem->getitem->getitem->getitem->getitem->getitem->False

  45. #__bool__():布尔测试 -> __len__()
  46. class D:
  47.     def __bool__(self):
  48.         print('bool')
  49.         return True

  50. d = D()
  51. print(bool(d))
  52. # bool
  53. # True

  54. class D:
  55.     def __init__(self, data):
  56.         self.data = data
  57.     def __len__(self):
  58.         print('len')
  59.         return len(self.data)

  60. d = D('fishc')
  61. print(bool(d))
  62. # len
  63. # True   长度非零则直接返回True
  64. d = D([])
  65. print(bool(d))
  66. # len
  67. # False

  68. #跟比较运算相关的魔法方法:P189   <   <=   >   >=   ==   !=
  69. class S(str):
  70.     def __lt__(self, other):
  71.         return len(self) < len(other)
  72.     def __gt__(self, other):
  73.         return len(self) > len(other)
  74.     def __eq__(self, other):
  75.         return len(self) == len(other)

  76. s1 = S('FishC')
  77. s2 = S('fishc')
  78. print(s1 < s2)#False
  79. print(s1 > s2)#False
  80. print(s1 == s2)#True
  81. print(s1 != s2)#True
  82. #__eq__()只会拦截等值判断(==),返回长度比较结果,不会拦截不等值判断(!=),故不等于号两边比较的依然是字符串ASCII码是否相同
  83. print(s1 <= s2)#True
  84. print(s1 >= s2)#False

  85. #如果不想让某个魔法方法生效,可以直接将其赋值为None
  86. class S(str):
  87.     def __lt__(self, other):
  88.         return len(self) < len(other)
  89.     def __gt__(self, other):
  90.         return len(self) > len(other)
  91.     def __eq__(self, other):
  92.         return len(self) == len(other)
  93.     __le__ = None
  94.     __ge__ = None
  95.     __ne__ = None

  96. s1 = S('FishC')
  97. s2 = S('fishc')
  98. try:
  99.     s1 != s2
  100. except TypeError as e:
  101.     print(e)
  102. #'NoneType' object is not callable

  103. #这种做法也适用与代偿实现,定义第一个魔法方法为None后可以使代偿不实现
  104. class C:
  105.     def __init__(self, data):
  106.         self.data = data
  107.     def __iter__(self):
  108.         print('iter', end='->')
  109.         self.i = 0
  110.         return self
  111.     def __next__(self):
  112.         print('next', end='->')
  113.         if self.i == len(self.data):
  114.             raise StopIteration
  115.         item = self.data[self.i]
  116.         self.i += 1
  117.         return item
  118.     __contains__ = None#__contains__ -> __iter__() + __next__() -> __getitem__()

  119. c = C([1, 2, 3, 4, 5])
  120. try:
  121.     print(3 in c)
  122. except TypeError as e:
  123.     print(e)
  124. #'C' object is not a container
复制代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-2-16 13:01:08 | 显示全部楼层

鱼油你好,我有些地方有点看不懂,不知能不能请你解惑一下。
你的第34行代码:按个使用next拿出数据比较,比如next拿出1,则item = 1,return item后执行什么操作呢?如果是3 in item的话好像不行吧,next()返回的类型是int,这样3 in 3 我试过了会报错。
还有就是为什么没找到会继续使用next寻找?而找到了又是如何输出True而不继续遍历完列表呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-2-23 11:03:53 | 显示全部楼层
Tzz123 发表于 2023-2-16 13:01
鱼油你好,我有些地方有点看不懂,不知能不能请你解惑一下。
你的第34行代码:按个使用next拿出数据比较 ...

return item 后比较 3 == item , 不相等就继续__next__ , 3 in  3 报错是因为后面的参数需要可迭代对象 ,没找到继续找是因为代偿机制 ,找到了就相当于 3 == 3 为True。  总结为一句话就是in运算符的机制需要后面的参数为可迭代对象。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

发表于 2023-8-17 17:21:59 | 显示全部楼层
## __contains\_\_(self, item)

用于实现成员关系的检测,对应的运算符是in 和 not in

```python
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
嗨~
True
6 in c
嗨~
False
```

### 代偿

Python在找不到`__iter__()`和`__next__()`魔法方法的情况下就会去查找`__getitem__()`方法

如果说没有实现`__contains__()`,但又使用了in和not in,namepython就会去查找`__iter__()`和`__next__()`魔法方法

```python
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
```

只要return和要找的值一样就结束了

如果没有`__iter__()`和`__next__()`,会去找`__getitem__()`的魔法方法

```python
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
```



## __bool\_\_()

```python
class D:
    def __bool__(self):
        print("Bool")
        return True

   
d = D()
bool(d)
Bool
True
```

代偿,如果没有定义`__bool__()`魔法方法,那么Python就会去找找是否存在`__len__()`这个魔法方法,返回值非零则是True

```python
class D:
    def __init__(self, data):
        self.data = data
    def __len__(self):
        print("Len")
        return len(self.data)

   
d =  D("Solo")
bool(d)
Len
True
bool(d)
Len
False
```

## 比较运算

1. <   `__lt__(self, other)`
2. <= `__le__(self, other)`
3. \>   `__gt__(self, other)`
4. \>= `__ge__(self, other)`
5. ==  `__eq__(self, other)`
6. !=   `__ne__(self, other)`

字符串默认比较编码值大小,修改为比较长度

```python
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

   
s1 = S("Solo")
s2 = S("solo")
s1 < s2
False
s1 > s2
False
s1 == s2
True
s1 != s2 ##没有定义!=方法
True
s1 <= s2
Traceback (most recent call last):
  File "<pyshell#235>", line 1, in <module>
    s1 <= s2
TypeError: 'NoneType' object is not callable
```

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2024-4-7 17:47:45 | 显示全部楼层
  def __contains__(self, name):
        for student in self.students.values():
            if student.name == name:
                return student

这里返回student,是不是相当于触发之后,要进行布尔运算,得出布尔值?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-6-4 17:51

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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