小甲鱼 发表于 2022-8-9 16:43:46

已有 24 人购买  本主题需向作者支付 5 鱼币 才能浏览 购买主题

小古比鱼 发表于 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,真是未雨绸缪的好主意啊!

migu_sm1 发表于 2022-12-7 23:02:22

Learning....
Support 小古比鱼


Ensoleile 发表于 2023-1-7 15:27:43

本帖最后由 Ensoleile 于 2023-1-13 00:22 编辑

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

c =C()
print(3 in c)
#hello
#True
#对应self.data,3对应__contains__()魔法方法中的item

#__contains__()代偿 P189 -> __iter__() + __next__() -> __getitem__()
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 += 1
      return item

c =C()
print(3 in c)
#iter->next->next->next->True
#首先调用iter将列表变为迭代器,再按个使用next拿出数据比较,直到找到的时候返回True
print(6 in c)
#iter->next->next->next->next->next->next->False

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

c = C()
print(3 in c)
# getitem->getitem->getitem->True
print(6 in c)
# getitem->getitem->getitem->getitem->getitem->getitem->False

#__bool__():布尔测试 -> __len__()
class D:
    def __bool__(self):
      print('bool')
      return True

d = D()
print(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')
print(bool(d))
# len
# True   长度非零则直接返回True
d = D([])
print(bool(d))
# len
# False

#跟比较运算相关的魔法方法:P189   <   <=   >   >=   ==   !=
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')
print(s1 < s2)#False
print(s1 > s2)#False
print(s1 == s2)#True
print(s1 != s2)#True
#__eq__()只会拦截等值判断(==),返回长度比较结果,不会拦截不等值判断(!=),故不等于号两边比较的依然是字符串ASCII码是否相同
print(s1 <= s2)#True
print(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')
try:
    s1 != s2
except TypeError as e:
    print(e)
#'NoneType' object is not callable

#这种做法也适用与代偿实现,定义第一个魔法方法为None后可以使代偿不实现
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 += 1
      return item
    __contains__ = None#__contains__ -> __iter__() + __next__() -> __getitem__()

c = C()
try:
    print(3 in c)
except TypeError as e:
    print(e)
#'C' object is not a container

Tzz123 发表于 2023-2-16 13:01:08

Ensoleile 发表于 2023-1-7 15:27
代偿

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

deep-cy 发表于 2023-2-23 11:03:53

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

return item 后比较 3 == item , 不相等就继续__next__ , 3 in3 报错是因为后面的参数需要可迭代对象 ,没找到继续找是因为代偿机制 ,找到了就相当于 3 == 3 为True。总结为一句话就是in运算符的机制需要后面的参数为可迭代对象。

Solo丶Hand 发表于 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()
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 += 1
      return item

   
c = C()
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

   
c = C()
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
```

亦又 发表于 2024-4-7 17:47:45

def __contains__(self, name):
      for student in self.students.values():
            if student.name == name:
                return student

这里返回student,是不是相当于触发之后,要进行布尔运算,得出布尔值?
页: [1]
查看完整版本: 第070讲:类和对象(XIII)