python面向对象编程(4)

428 阅读7分钟
python重载运算符详解
  1. 基础

    • 运算符重载能够让类拦截正常的python操作

    • 类能够重载所有的python表达式操作

    • 类也能够重载内置操作,如打印、函数调用抑或是属性访问

    • 重载能够让类的实例看上去更像是内置类型

    • 重载是实现类中提供的特殊方法名,如__X__

  2. 索引和切片操作,__getitem__ & __setitem__

  • __getitem__方法被调用的应用场景

对象实例根据索引值获取索引值对应的数据

# indexer.py# 注:python如果没有特别说明表示都是3.x版本class Indexer:
    def __init__(self):
        print("call init ...")
        self.__data = [1,2,3]            def __getitem__(self, index):           # index为索引值
        print("call getitem index...")                return self.__data[index]def test_index():
    # 取值
    for i in range(3):
        print(X[i])  # 等价于Indexer.__getitem__(X,i)if __name__ == '__main__':
    test_index()>>> python indexer.py   

对象实例根据分片方式获取一组分片索引值对应的数据

# slice.pyclass SliceObject:
    def __init__(self):
        print("call slice object init ...")
        self.__data = [1,2,3,4,5,6]            def __getitem__(self, index):   # index为索引值或者分片对象
        if isinstance(index, int):
            print('indexing', index)                else:            # 分片对象有start stop 以及step属性
            print('slicing', index.start, index.stop, index.step)         return self.__data[index]def test_slice():
    X = SliceObject()    # 分片取值
    print(X[0:2])
    print(X[:-1])
    print(X[1:])if __name__ == '__main__':
    test_slice()>>> python slice.py

迭代器调用并进行数据遍历

# indexer.py def test_iterator():
    X = Indexer()        for item in X:
        print(item)if __name__ == '__main__':    # test_index()
    test_iterator()>>> python indexer.py

py2.x分片操作的内置方法,py3.x已经删除

# slice.py class Slicer:
    """
    py2.x 分片
    """
    def __getitem__(self, index):
        print("call __getitem__ for index:%s" % str(index))            def __getslice__(self, i, j):
        print("call __getslice__ for start[%d] to end[%d]" % (i,j))            def __setslice__(self, i, j, seq):
        print("call __setslice__ for start[%d] to end[%d] with seq[%d]" % (i,j,seq))    def test_slicer():
    Slicer()[1]
    Slicer()[1:9]
    Slicer()[1:9:2]if __name__ == '__main__':
    test_slicer()>>> python slice.py

py3.x新增__index__内置方法,该方法表示返回一个实例对象的整数型,与索引切片没关系

# indexer.pyclass Indexing:
    """
    py3.x 新增的内置方法,但是该操作不是表示索引操作,而是为一个对象实例返回一个整型数值,并且用于转换为数值字符串的内置操作
    """
    def __index__(self):
        return 200    def test_indexing():
        X = Indexing()    # 将对象转换为数值字符串,这里执行两个内置方法,__index__ 和 __str__
        print(hex(X))if __name__ == '__main__':
    test_indexing()>>> python indexer.py

  • __setitem__对索引属性进行赋值操作

# indexer.pyclass Indexer:
    ... 
    def __setitem__(self, index, value):    # index恒为索引下标,value为值
        print("call setitem ...")
        print("key[%s]--value[%s]" % (index,value))
        self.__data.insert(index,value)def test_index():
    # 设置值
    X = Indexer()
    X[0] = 1  # 等价于Indexer.__setitem__(X,0,1)if __name__ == '__main__':
    test_index()>>> python indexer.py

 3.迭代器对象:__iter__ & __next__

自定义迭代器

# iterator.pyclass Squares:
    def __init__(self, start, stop):
        self.value = start - 1
        self.stop = stop            def __iter__(self):
        # 生成迭代器对象
        print("call iter ...")                return self            def __next__(self):
        if self.value == self.stop:                        raise StopIteration
        print("call next for value:", end='')
        self.value += 1
        return self.value ** 2def test():
    X = Squares(1, 5)    # 迭代器不支持使用索引
    X[1]    # TypeError: 'Squares' object does not support indexingif __name__ == '__main__':
    test()>>> python iterator.py

使用循环迭代器

# iterator.pydef test_for():
    # 循环使用迭代器
    for item in Squares(2, 9):  # 调用Squares.__iter__,生成迭代器对象
        print(item)             # 调用Squares.__next__>>> python iterator.py

逐个进行迭代

# iterator.pydef test_single():
    # 逐个调用迭代器
    X = Squares(2, 9)    # 显示将X转换为迭代器对象,条件是该对象有实现__iter__,只需调用一次
    I = iter(X)             # Squares.__iter__
    print(next(I))          # 调用Squares.__next__
    print(next(I))          # 调用Squares.__next__if __name__ == '__main__':
    test_single()>>> python iterator.py

迭代器单次扫描

# iterator.pydef single_scan():
    print("start single scan ....")
    X = Squares(1, 2)    # 首次会执行__iter__对迭代器进行初始化,
    # 并且每次执行将调用__next__将数据返回并保存当前迭代器的状态,直至为不可迭代状态
    list1 = [n for n in X]
    print(list1)    # 迭代器当前是不可迭代的状态,即list2是空列表
    list2 = [n for n in X]       # now list2 is empty
    print(list2)
    print("end single scan ....")if __name__ == '__main__':
    single_scan()>>> python iterator.py

迭代器多次扫描

# iterator.pydef mutil_scan():
    print("start mutil scan ... ")        # 方式一:每次都是重新创建新的迭代器来进行迭代2.7没有next方法,不可用
    list1 = [n for n in Squares(1,2)]
    print(list1)
    list2 = [n for n in Squares(1, 2)]
    print(list2)            # 方式二 转换为列表并用列表解析器来转换
    list_iter = list(Squares(1,2))
    list3 = [n for n in list_iter]
    print(list3)
    list4 = [n for n in list_iter]
    print(list4)
    print("end mutil scan ...")if __name__ == '__main__':
    mutil_scan()>>> python iterator.py

设计可多次迭代的类模板

  • 自定义类可多次迭代

    • 条件:在内置方法中__iter__中返回的迭代器应当新的迭代器对象,而不是self自身对象

# iterator.pyclass MutilIterator:
    def __init__(self, wrapper):
        self.__wrapper = wrapper            def __iter__(self):
        """
         每次调用迭代器的时候就创建一个新的迭代器并返回
        """
        return DefineIterator(self.__wrapper)class DefineIterator:
    def __init__(self, wrapped):
        self.wrapped = wrapped
        self.offset = 0

    def __next__(self):
        if self.offset >= len(self.wrapped):                        raise StopIteration                else:
            item = self.wrapped[self.offset]
        self.offset += 1
        return item    #     py2.x 没有next方法,兼容python2.x 和 python3.x
    next = __next__def test_mutil_object_iter():
    S = "abcd"
    # 索引遍历操作
    M = MutilIterator(S)        for x in M:        print("recreate iterator for y loop by creating M iterator ...")                      for y in M:               # 第二次调用迭代器获取新的迭代器对象 
            print(x+y,end = ",")
        print("")        if __name__ == '__main__':
    test_mutil_object_iter()>>> python iterator.py

  • 类的迭代器与生成器混搭

# iterator.pyclass MixIterator:
    def __init__(self,start,stop):
        self.__start = start
        self.__stop = stop            def __iter__(self):
        for index in range(self.__start,self.__stop+1):                        yield index**2      # 返回一个生成器对象,通过调用next()来显示值def test_mix_iterator():
    iterator = MixIterator(1,5)            # 同一个对象可以迭代多次
    list1 = [index for index in iterator]  
    list2 = [index for index in iterator]
    print(list1)
    print(list2)if __name__ == '__main__':
    test_mix_iterator()>>> python iterator.py

  • 重构上述设计类的迭代器

# iterator.pyclass MutilIterator2:
    def __init__(self,wrapper):
        self.__wrapper = wrapper            def __iter__(self):
        print("call iter ... ")
        offset = 0
        while offset < len(self.__wrapper):
            item = self.__wrapper[offset]
            offset+=1
            yield itemdef test_mutil2_iterator():
    S = "abcd"
    M = MutilIterator2(S)        for x in M:       # 执行__iter__方法
        for y in M:    # 执行__iter__方法,进入方法的时候offset又变为初始化0
            print(x+y,end = ",")
        print()if __name__ == '__main__':
    test_mutil2_iterator()>>> python iterator.py

迭代器小结

  • 迭代器是一个很强大的工具,能够让我们创建自定义迭代器对象来完成自己的业务需求,比如支持大型数据库在多个游标中进行数据查询时获取迭代

  • 对象单个迭代器对象,即不能进行多次迭代取值操作,python有map、zip、函数生成器以及生成器表达式

  • 对象多个迭代器对象,即可以来多次迭代取值动作,python有list、string、range

  • 使用类的迭代器,我们可以自定义迭代器是单例还是多例