第二十九章 运算符重载

#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

  

原文地址:https://www.cnblogs.com/szn409/p/6776167.html