Python进阶4

作者: MetaT1an | 来源:发表于2019-01-14 17:08 被阅读3次

    自定义序列类型

    序列类型的分类

    按照序列存储数据的类型

    • 容器序列:list, tuple, deque
    • 扁平序列:str, bytes, array.array, bytearray

    按照序列是否可变

    • 可变序列:list, deque, array.array, bytearray
    • 不可变序列:str, tuple, bytes

    序列的abc继承关系

    引言

    • collections.abc模块中有很多内置的抽象基类
    • 序列主要和两个抽象基类有关:SequenceMutableSequence

    细节

    Sequence

    """
    Sequence 抽象基类
    它继承了Sized Iterable Contain 这三个抽象基类
    """
    class Sequence(Sized, Iterable, Container):
    
        """All the operations on a read-only sequence.
    
        Concrete subclasses must override __new__ or __init__,
        __getitem__, and __len__.
        """
    
        __slots__ = ()
    
        @abstractmethod
        def __getitem__(self, index):
            raise IndexError
    
        def __iter__(self):
            i = 0
            try:
                while True:
                    v = self[i]
                    yield v
                    i += 1
            except IndexError:
                return
    
        def __contains__(self, value):
            for v in self:
                if v == value:
                    return True
            return False
    
        def __reversed__(self):     # 该魔法函数使得序列类型可以反转
            for i in reversed(range(len(self))):
                yield self[i]
    
    class Sized(metaclass=ABCMeta):
    
        __slots__ = ()
    
        @abstractmethod
        def __len__(self):      # 这个魔法函数使得序列类型可以使用 len()获得长度
            return 0
    
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Sized:
                if any("__len__" in B.__dict__ for B in C.__mro__):
                    return True
            return NotImplemented
    
    class Iterable(metaclass=ABCMeta):
    
        __slots__ = ()
    
        @abstractmethod
        def __iter__(self):     # 这个魔法函数使得序列类型可以进行 for 循环
            while False:
                yield None
    
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Iterable:
                if any("__iter__" in B.__dict__ for B in C.__mro__):
                    return True
            return NotImplemented
    
    class Container(metaclass=ABCMeta):
    
        __slots__ = ()
    
        @abstractmethod
        def __contains__(self, x):  # 这个魔法函数使得序列类型可以使用 in 操作符
            return False
    
        @classmethod
        def __subclasshook__(cls, C):
            if cls is Container:
                if any("__contains__" in B.__dict__ for B in C.__mro__):
                    return True
            return NotImplemented
    

    MutableSequence

    class MutableSequence(Sequence):
    
        __slots__ = ()
    
        """All the operations on a read-write sequence.
    
        Concrete subclasses must provide __new__ or __init__,
        __getitem__, __setitem__, __delitem__, __len__, and insert().
    
        """
    
        @abstractmethod
        def __setitem__(self, index, value):
            raise IndexError
    
        @abstractmethod
        def __delitem__(self, index):
            raise IndexError
            
        def __iadd__(self, values):     # 使得序列可以使用 += 运算符号进行计算
            self.extend(values)
            return self
        
        # ......只列出几个有代表意义的魔法函数......
        # 这些魔法函数使得序列类型可以改变
    

    序列的+、+=和extend

    引言

    • 对于可变序列,我们有三种方式来扩充原始序列
    • 三种方式有原理效率上的不同,我们要根据实际情况选择合适的方式来使用

    使用范例

    """
    普通的加法会在内存中产生一个新的序列对象
    时间 空间开销相对比较大
    必须要是类型完全一致才能够使用这种合并方式
    """
    a, b, c = [1, 2], [3, 4], (5, 6)
    
    d = a + b   # [1, 2] + [3, 4]
    print(d)
    
    # result:
    # [1, 2, 3, 4]
    
    d = a + c   # [1, 2] + (5, 6)
    print(d)
    
    # result:
    # TypeError: can only concatenate list (not "tuple") to list
    
    """
    += 可以理解为原地加,单纯地在原对象上进行修改,效率比 + 要高很多
    这个功能背后有一个魔法函数,__iadd__(self, values)
    上一小节中有这个函数具体的实现,我们可以看到,它是调用了 extend()方法
    所以本质上,它和调用 extend()函数产生的效果是一模一样的
    """
    a, b, c = [1, 2], [3, 4], (5, 6)
    
    a += b
    print(a)
    
    # result:
    # [1, 2, 3, 4]
    
    a += c
    print(a)
    # result:
    [1, 2, 3, 4, 5, 6]
    

    鸭子类型那一小节,我们分析了extend()函数的参数要求,只要是可迭代类型都能够作为参数扩充列表。

    +=操作本质上就是调用 extend() 函数,所以能将列表元组进行合并也就不奇怪了,因为他们本质上都是可迭代的类型。

    实现可切片的对象

    引言

    • 切片介绍
    # 使用模式:[start:end:step]
    """
    start: 切片开始的位置,默认是 0
    end: 切片截止(不包括)的位置,默认是列表长度
    step: 切片的步长,默认是 1
    start和end为默认值时可以省略不写,step为默认值时可以连同最后的冒号一起省略
    step为负数时,表示反向切片,此时需满足: start > end
    """
    
    • 切片举例
    li = [8, 4, 3, 2, 1, 7]
    li[::]      # 返回原序列
    li[::-1]    # 返回原序列的逆序列表
    li[::2]     # 隔一个取一个,所有偶数位置
    li[1::2]    # 隔一个取一个,所有奇数位置
    li[3:6]     # 取[3, 6)区间内的所有元素,左包含,右不包含
    li[0:666]   # 结束位置大于列表长度,从尾部截断,返回原列表
    li[666:]    # 开始位置大于列表长度,直接返回空列表
    
    li = [6, 6]
    li[:0] = [1, 2]     # 列表头部插入元素 [1, 2, 6, 6]
    li[1:1] = [1, 1]    # 列表的某一位置插入元素 [6, 1, 1, 6]
    li[:1] = [1, 1]     # 替换列表元素 [1, 1, 6]
    li[1:] = [1, 1]     # 替换列表元素 [6, 1, 1]
    

    使用案例

    class Group:
        def __init__(self, group_name, staffs):
            self.group_name = group_name
            self.staffs = staffs
            
        def __reversed__(self):     # reversed(group)
            self.staffs.reverse()
            
        """
        在使用切片和索引访问操作时,相关的参数会传递到 __getitem__(self, item) 中
        我们通常希望,切片返回的对象和原对象是同一种类型
        使用切片的话,传入的item是一个slice类型
        使用索引访问,传入的item是一个int类型
        """
        def __getitem__(self, item):  # 对象可切片的关键
            cls = type(self)    # 获得该实例的类型
            if isinstance(item, slice):
                return cls(self.group_name, self.staffs[item])  # 委托给列表实现切片
            elif isinstance(item, int):
                return self.staffs[item]
        
        def __len__(self):
            return len(self.staffs)
            
        def __iter__(self):
            return iter(self.staffs)    # 后面会详细讲解
            
        def __contains__(self, item):
            return item in self.staffs   # 同样委托给列表的in操作来实现
        
        def __str__(self):
            return "name:{name}\nstaffs:{staffs}".format(name=self.group_name, staffs=self.staffs)
    
    
    staffs = ["MetaTian0", "MetaTian1", "MetaTian2"]
    group = Group("HIT", staffs)
    
    print(group[1])     # 索引访问
    print(group[0:2])   # 切片
    reversed(group)     # 反转
    print(group)
    print(len(group))   # 长度
    print("MetaTian0" in group, "MetaTian6" in group)   # in 操作符
    
    # result:
    # MetaTian1
    # name:HIT; staffs:['MetaTian0', 'MetaTian1']
    # name:HIT; staffs:['MetaTian2', 'MetaTian1', 'MetaTian0']
    # 3
    # True False
    

    bisect模块

    引言

    • 如果我们在一个有序序列中需要增加一个元素,但任然要维持序列的有序性,可以使用append()添加元素,再调用sort()来重新排序。
    • bisect模块用来维持已排序序列(升序)的顺序,效率更高。
    • 两个关键函数insort()bisect

    使用案例

    import bisect
    
    int_list = []
    """
    在插入过程中维护序列的有序性
    """
    bisect.insort(int_list, 2)
    bisect.insort(int_list, 6)
    bisect.insort(int_list, 1)
    bisect.insort(int_list, 3)
    bisect.insort(int_list, 5)
    
    print(int_list)
    
    # result:
    # [1, 2, 3, 5, 6]
    
    """
    查询一个元素在序列中应该插入的位置
    bisect(): 同值元素默认插在右侧
    bisect_left(): 同值元素默认插在左侧
    """
    print(bisect.bisect(int_list, 3))
    print(bisect.bisect_left(int_list, 3))
    
    # result:
    # 3
    # 2
    

    列表一定是最好的吗

    引言

    使用案例

    import array
    """
    使用前要引入array模块,要申明存储的对象类型
    """
    
    my_array = array.array("i")
    my_array.append(666)
    my_array.append(4)
    my_array.extend([1, 2, 3])
    my_list = my_array.tolist()
    
    # 大部分基本的操作函数和序列差不多,这里就不做详细介绍
    print(my_array)
    
    # result:
    # array('i', [666, 4, 1, 2, 3])
    # [666, 4, 1, 2, 3]
    

    列表推导、生成器表达式、字典推导

    引言

    • 推导表达式是一种更加简洁高效,生成相关对象的一种方式

    使用案例

    """
    列表推导
    生成1-20中的奇数
    """
    # 普通方式
    odd_list = []
    for i in range(1, 21):
        if i%2 == 1:
            odd_list.append(i)
    
    # 列表推导
    odd_list = [i for i in range(1, 21) if i%2 == 1]
    
    """
    生成器表达式
    后面部分会详细介绍其原理
    """
    odd_list = (i for i in range(1, 21) if i%2 == 1)
    print(type(odd_list))
    
    # result:
    # <class 'generator'>
    
    for item in odd_list:
        print(item, end=' ')
        
    # result:
    # 1 3 5 7 9 11 13 15 17 19 
    
    """
    字典推导式
    """
    my_dict = {"a":1, "b":2, "c":3}
    reversed_dict = {val:k for k, val in my_dict.items()}
    print(reversed_dict)
    
    # result:
    # {1: 'a', 2: 'b', 3: 'c'}
    

    相关文章

      网友评论

        本文标题:Python进阶4

        本文链接:https://www.haomeiwen.com/subject/jmjjdqtx.html