#1. #A:运算符重载只是意味着在类方法中拦截内置的操作,当类的实例出现在内置操作中,python会自动调用重载的方法 #B:__init__构造函数是最常见的运算符重载 #2. #A:常见的运算符重载方法: ''' __init__ 构造函数 __del__ 析构函数 __add__ + __or__ | __repr__, __str__ 打印,转换 __call__ 函数调用 __getattr__ 点号运算 __setattr__ 属性赋值语句 X.any = value __delattr__ 属性删除 del X.any __getattribute__ 属性获取 X.any __getitem__ 索引运算 __setitem__ 索引赋值 __delitem__ 索引和分片删除 del X[key] X[i:j] = sequence __len__ 长度 __It__, __gt__, __le__, __ge__, __eq__, __ne__ X < Y, X > Y, X <= Y, X >= Y, X == Y, X != Y __radd__ 右侧加法 other + X __iadd__ 增强加法 X += Y __iter__, __next__ 迭代环境 __contains__ 成员关系测试 __index__ 整数值 __get__, __set__ 描述符属性 __delete__, __new__ 创建和删除 __sub__ 减法 __enter__, __exit__ 环境管理器 ''' #3. #A:一些内置的操作,如打印,有默认的重载方法,继承于python3.0中隐含的object类中 #4. #A:索引和分片:__getitem__, __setitem__ #B:除了索引,对于分片表达式也调用__getitem__ #C:__getitem__也可以是python中一种重载迭代的方式 class CTest(): L0 = [1, 2, 3] def __getitem__(self, index): print(index) return CTest.L0[index] def __setitem__(self, index, value): print(index) CTest.L0[index] = value Test = CTest() value = Test[0] #value = 1 输出0 Test[0] = 'a' #输出0 value = CTest.L0 #value = ['a', 2, 3] value = Test[0:2] #value = ['a', 2] 输出slice(0, 2, None) Test[0:2] = 'abc' value = Test.L0 #value = ['a', 'b', 'c', 3] 输出slice(0, 2, None) L0 = [] for value in CTest(): L0.append(value) #L0 = ['a', 'b', 'c', 3] 输出0 1 2 3 4 #5. #A:尽管__getitem__能在迭代环境中使用,但是这只是退而求其次的方法 #B:python中的迭代环境都会先尝试__iter__方法,再尝试__getitem__ #C:迭代环境是通过调用内置函数iter去尝试寻找__iter__方法来实现的,而这种方法应该返回一个迭代器对象 # 如果提供了,python就会重复调用这个迭代器对象的next方法,直到发送StopIterator异常 # 如果没找到这类的__init__方法,python就会改用__getitem__机制,直到发生IndexError异常 class CTest(): count0 = 0 count1 = 0 def __init__(self, start, stop): self.start = start self.stop = stop def __iter__(self): CTest.count0 += 1 return self def __next__(self): CTest.count1 += 1 if self.start == self.stop: raise StopIteration else: self.start += 1 return self.start ** 2 Test = CTest(0, 5) L0 = [] for value in Test: L0.append(value) #L0 = [1, 4, 9, 16, 25] value = CTest.count0, CTest.count1 #value = (1, 6) try : value = next(Test) except StopIteration: pass #运行至此 #对于上述类,一个类实例的迭代器只有一种状态,可以创建一个类,其实例的迭代器有多个状态 class CIter(): def __init__(self, value): self.value = value self.start = 0 def __next__(self): if self.start == len(self.value): raise StopIteration else: value = self.value[self.start] self.start += 1 return value class CTest(): def __init__(self, value): self.value = value def __iter__(self): return CIter(self.value) Test = CTest("abc") L0 = [] for i in Test: for j in Test: L0.append(i + j) #L0 = ['aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc'] #对于这个类,一个类的实例可以拥有多个状态 #6. #A:运算符重载往往是多个层级的:类提供特定的方法或者提供退而求其次的更通用的替代方案 # 布尔测试会先尝试一个特定的__bool__,如果没有则会尝试__len__ class CTest(): def __init__(self, value): self.value = value def __bool__(self): return bool(self.value) def __len__(self): return len(self.value) def __contains__(self, value): return value in self.value Test = CTest('abc') b0 = bool(Test) #b0 = True v0 = len(Test) #v0 = 3 b1 = 'b' in Test #b1 = True #7. #A:__getattr__方法是拦截属性点号运算的,当类实例使用点号访问未定义属性的时候就会调用此方法,若访问的属性在继承树中存在则不会调用 #B:__setattr__方法会拦截所有属性的赋值语句,在此函数中对self进行赋值会导致无限递归,所以要使用实例的属性字典进行赋值 #C:可以利用__setattr__来对属性赋值进行拦截,来防止在类外部对类实例的属性进行更改,从而达到类似于属性的private属性 class CTest(): def __init__(self): self.value = 'abc' def __getattr__(self, str): print(str + " is not exist") def __setattr__(self, str, value): #self.str = value 会导致无限循环 self.__dict__[str] = value + '__setattr__' Test = CTest() Test.value #并不会产生输出 Test.str #输出str is not exist Test.value = 'szn' v0 = Test.value #v0 = 'szn__setattr__' b0 = 'str' in Test.__dict__ #b0 = False Test.str = 'str' b1 = 'str' in Test.__dict__ #b1 = True #8. #A:__getattribute__方法会拦截所有的属性获取,而非仅仅是未定义的,此方法很容易导致无限递归 #B:__getattribute__中要用object,不然会陷入无穷递归 class CTest(): def __init__(self): self.value = 'abc' def __getattribute__(self, str): if 'value' == str: return object.__getattribute__(self, str) else: return str Test = CTest() v0 = Test.str #v0 = 'str' v1 = Test.value #v1 = 'abc' #9. #A:打印操作会首先尝试__str__然后尝试__repr__ #B:__str__, __repr__都必须返回字符串,其他结果类型不会转换并会引发错误 class CTest(): def __repr__(self): return 'repr' Test = CTest() str0 = str(Test) #str0 = 'repr' str1 = repr(Test) #str1 = 'repr' class CTest(): def __str__(self): return 'str' Test = CTest() str0 = str(Test) #str0 = 'str' str1 = repr(Test) #str1 = '<__main__.CTest object at 0x0000000002E3B630>' #10. #A:__radd__右侧加法, __iadd__增强加法, __add__加法 #B:当两个类实例相加时,先调用__add__,然后通过简化左边的运算数来除法__radd__ #C:每个二元运算都有类似的右侧和原处重载方法 class CTest(): def __init__(self): self.str = '' def __add__(self, value): print('a') return self.str + value def __iadd__(self, value): print('i') self.str += value return self def __radd__(self, value): print('r') return self.str + value Test = CTest() v0 = Test + 'a' #v0 = 'a' 输出a v1 = 'b' + Test #v1 = 'b' 输出r Test += 'c' #输出i s0 = Test.str #s0 = 'c' v2 = CTest() + CTest() #v2 = '' 输出a r #11. #A:__call__函数调用运算符 #B:__del__析构函数 class CTest(): def __call__(self, str): return str def __del__(self): print("del ") Test = CTest() str = Test('a') #str = 'a' Test = 'a' #输出del