码迷,mamicode.com
首页 > 其他好文 > 详细

第五章 自定义序列类

时间:2020-07-20 15:46:26      阅读:58      评论:0      收藏:0      [点我收藏+]

标签:coroutine   metaclass   概念   require   expr   Staff   列表操作   声明   而且   

5.1 python中的序列分类

序列是python中一个很重要的协议。

第一个维度:

容器序列(list、tuple、deque)

容器序列可以放任意类型的数据,所以理解为容器

my_list = []
my_list.append(1) # int
my_list.append("a") # str
print(my_list)
[1, ‘a‘]

扁平序列(str、bytes、bytearray、array.array)

str是序列类型,有个特性是可以用for循环遍历。

第二个维度:

可变序列(list、deque、bytearray、array)

不可变序列(str、tuple、bytes)

5.2 序列类型的abc继承关系

跟容器相关的一些数据结构的抽象基类都是放在collections.abc里,点进去看

__all__ = ["Awaitable", "Coroutine",
           "AsyncIterable", "AsyncIterator", "AsyncGenerator",
           "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
           "Sized", "Container", "Callable", "Collection",
           "Set", "MutableSet",
           "Mapping", "MutableMapping",
           "MappingView", "KeysView", "ItemsView", "ValuesView",
           "Sequence", "MutableSequence",
           "ByteString",
           ]

里面都是抽象基类,"Sequence"(不可变序列)、 "MutableSequence"(可变序列),是序列相关的抽象基类。

点击去Sequence看,所有魔法函数构成了序列协议:

class Sequence(Reversible, Collection):

    """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 is value or v == value:
                return True
        return False

    def __reversed__(self):
        for i in reversed(range(len(self))):
            yield self[i]

Sequence继承了两个类,Reversible是数据的反转

点进去看Collection

class Collection(Sized, Iterable, Container):

    __slots__ = ()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Collection:
            return _check_methods(C,  "__len__", "__iter__", "__contains__")
        return NotImplemented

继承了三个抽象基类Sized, Iterable, Container

class Sized(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            return _check_methods(C, "__len__")
        return NotImplemented

Sized实现了len方法,这样可以计算Collection的长度

class Iterable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            return _check_methods(C, "__iter__")
        return NotImplemented

Iterable实现了迭代器,可以进行for循环

class Container(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            return _check_methods(C, "__contains__")
        return NotImplemented

Container实现了contains,可以使用if、in判断。当然如果不实现contains,使用getitem魔法方法,也是可以用if、in判断的。

再看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
    ......

里面有setitem、delitem魔法函数,分别是添加和删除,还有很多其他的魔法函数,这些魔法函数构成了序列的协议。

5.3 list中+、+=和extend的区别

a = [1,2]
c = a + [3,4]

print(C)
[1, 2, 3, 4]

+可以把两个list做连接,+=也可以把两个list做连接

a = [1,2]
a += [3,4] # 就地加
print(a)
[1, 2, 3, 4]

+=还可以连接列表和元组

a = [1,2]
a += (3,4) # 就地加
print(a)
[1, 2, 3, 4]

还是一样的效果,但如果把+中的列表改为元组

a = [1,2]
c = a + (3,4)

print(C)
    c = a + (3,4)
TypeError: can only concatenate list (not "tuple") to list

会抛出异常,所以+和+=有很大的区别。

+=中可以接任意的序列类型,+=其实是通过一个魔法函数实现的。

MutableSequence(可变序列类型)里面有个__iadd__魔法函数:

def __iadd__(self, values):
    self.extend(values)
    return self

调用的是extend方法:

def extend(self, values):
    ‘S.extend(iterable) -- extend sequence by appending elements from the iterable‘
    if values is self:
        values = list(values)
    for v in values:
        self.append(v)

extend接受一个values,调用for循环,任何序列类型都可以调用for循环,然后依次append进列表。

那么使用extend也是可以的:

a = [1,2]
a += (3,4)
a.extend(range(3))
print(a)
[1, 2, 3, 4, 0, 1, 2]

再说append方法,和extend是不一样的

a = [1,2]
a.append([1,2])
a.append((3,4))
print(a)
[1, 2, [1, 2], (3, 4)]

append是把列表变成了一个元素加入列表中,而不是迭代里面的元素,依次加入列表中。

append不会去做for循环。

5.4 实现可切片的对象

先看下切片操作

#模式[start:end:step]
"""
    其中,第一个数字start表示切片开始位置,默认为0;
    第二个数字end表示切片截止(但不包含)位置(默认为列表长度);
    第三个数字step表示切片的步长(默认为1)。
    当start为0时可以省略,当end为列表长度时可以省略,
    当step为1时可以省略,并且省略步长时可以同时省略最后一个冒号。
    另外,当step为负整数时,表示反向切片,这时start应该比end的值要大才行。
"""
aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
print (aList[::])  # 返回包含原列表中所有元素的新列表
print (aList[::-1])  # 返回包含原列表中所有元素的逆序列表
print (aList[::2])  # 隔一个取一个,获取偶数位置的元素
print (aList[1::2])  # 隔一个取一个,获取奇数位置的元素
print (aList[3:6])  # 指定切片的开始和结束位置
aList[0:100]  # 切片结束位置大于列表长度时,从列表尾部截断
aList[100:]  # 切片开始位置大于列表长度时,返回空列表
[3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
[17, 15, 13, 11, 9, 7, 6, 5, 4, 3]
[3, 5, 7, 11, 15]
[4, 6, 9, 13, 17]
[6, 7, 9]
[0, 2, 0, 4, 0, 6]
aList = [3, 4, 5, 6, 7, 9, 11, 13, 15, 17]
aList[len(aList):] = [9]  # 在列表尾部增加元素
print("1-",aList)

aList[:0] = [1, 2]  # 在列表头部插入元素
print("2-",aList)

aList[3:3] = [4]  # 在列表中间位置插入元素
print("3-",aList)

aList[:3] = [1, 2]  # 替换列表元素,等号两边的列表长度相等
print("4-",aList)

aList[3:] = [4, 5, 6]  # 等号两边的列表长度也可以不相等
print("5-",aList)

aList[::2] = [0] * 3  # 隔一个修改一个
print ("6-",aList)

aList[::2] = [‘a‘, ‘b‘, ‘c‘]  # 隔一个修改一个
print("7-",aList)
# aList[::2] = [1,2]  # 左侧切片不连续,等号两边列表长度必须相等

aList[:3] = []  # 删除列表中前3个元素
print("8-",aList)

del aList[:3]  # 切片元素连续
print("9-",aList)

del aList[::2]  # 切片元素不连续,隔一个删一个
print("10-",aList)
1- [3, 4, 5, 6, 7, 9, 11, 13, 15, 17, 9]
2- [1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 15, 17, 9]
3- [1, 2, 3, 4, 4, 5, 6, 7, 9, 11, 13, 15, 17, 9]
4- [1, 2, 4, 4, 5, 6, 7, 9, 11, 13, 15, 17, 9]
5- [1, 2, 4, 4, 5, 6]
6- [0, 2, 0, 4, 0, 6]
7- [‘a‘, 2, ‘b‘, 4, ‘c‘, 6]
8- [4, ‘c‘, 6]
9- []
10- []

实现一个支持切片的对象(不可变),根据Sequence抽象基类里的魔法函数。

import numbers

class Group:
    # 支持切片操作
    def __init__(self, group_name, company_name, staffs):  # 组名,公司名,员工
        self.group_name = group_name
        self.company_name = company_name
        self.staffs = staffs  # staffs是list

    def __reversed__(self):
        self.staffs.reverse()

    def __getitem__(self, item):
        cls = type(self)  # 获取当前class
        # 返回一个Group类型

        if isinstance(item, slice):
            return cls(group_name=self.group_name,
                       company_name=self.company_name, 
                       staffs=self.staffs[item])
        elif isinstance(item, numbers.Integral):
            return cls(group_name=self.group_name,
                       company_name=self.company_name, 
                       staffs=[self.staffs[item]])

    def __len__(self):
        return len(self.staffs)

    def __iter__(self):
        return iter(self.staffs)

    def __contains__(self, item):
        if item in self.staffs:
            return True
        else:
            return False

staffs = ["bobby1", "imooc", "bobby2", "bobby3"]
group = Group(company_name="imooc", group_name="user", staffs=staffs)
# 切片
sub_group = group[:2]
# 求长度
print(len(group))
# is、in判断
if "bobby1" in group:
    print ("yes")
# for 迭代
for user in group:
    print(user)
# 反转
reversed(group)
print(group.staffs)
4
yes
bobby1
imooc
bobby2
bobby3
[‘bobby3‘, ‘bobby2‘, ‘imooc‘, ‘bobby1‘]

技术图片

Group是组的概念,在生活中很常见,比如学校的学习组,公司的部门。对Group实例切片,返回一个Group类型的对象。

getitem是切片的关键,item可以是一个slice对象,也可以是一个int。

sub_group是一个Group类型对象。

5.5 bisect维护已排序序列

用来处理已排序的序列,用来维持已排序的升序序列。

点进bisect里看:

技术图片

有insort和bisect,insort是用来插入的。

对数据结构了解的话,对已排序的序列,有一种查找效率很高的算法叫二分查找,bisect就是用二分查找来维持已排序序列,包括插入一个数据(insort)、查找数据应该插入什么位置(bisect)。

insort有两个变种的函数insort_right和insort_left,是insort_right的默认

  • insort_left:如果插入的数据在序列中已存在,则新数据插入已存在数据之前
  • insort_right:如果插入的数据在序列中已存在,则新数据插入已存在数据之后

bisect也有两个变种的函数bisect_left和bisect_right,是bisect_right的默认

  • bisect_left:一个数据插入序列的时候,插入位置做左边开始计算
  • bisect_right:一个数据插入序列的时候,插入位置做右边开始计算
import bisect

#用来处理已排序的序列,用来维持已排序的序列, 升序
#二分查找
inter_list = []
bisect.insort(inter_list, 3)
bisect.insort(inter_list, 2)
bisect.insort(inter_list, 5)
bisect.insort(inter_list, 1)
bisect.insort(inter_list, 6)

print(inter_list)
[1, 2, 3, 5, 6]

返回一个排序的序列,所以如果要维护一个排序好的序列,建议使用bisect插入数据,而且二分查性能很高。

print(bisect.bisect(inter_list, 3))
print(bisect.bisect_left(inter_list, 3))
3
2

可以知道3在序列中应该插入的位置是什么。

5.6 什么时候不应该用列表

平时用的最多的是list,但有时候会有更好的选择,array和deque。

array是c语言中的数组,性能很高。

先来看一下list,是用c语言写的,里面有很多方法。

技术图片

再来看一下array,也是用c语言写的。

array和list的一个重要区别, array只能存放指定的数据类型。list是一个容器,可以存放任何类型的数据,但是array在使用的时候一开始就要声明存放的数据类型,并且之后也只能存储对应类型的数据。

技术图片

array也有很多方法,有很多跟list是一样的。

技术图片

import array

my_array = array.array("i") # int类型
my_array.append(1)
my_array.append("abc")
array(‘i‘, [1])
    my_array.append("abc")
TypeError: an integer is required (got type str)

存放非int数据会报错。

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

列表生成式:用一行代码生成列表,性能高于列表操作

比如要提取出1-20之间的奇数,用for循环可以实现

odd_list = []
for i in range(21):
    if i %2 == 1 :
        odd_list.append(i)
print(odd_list)
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

那使用列表生成式呢?

odd_list= [ i for i in range(21) if i % 2 == 1]
print(odd_list)
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

比如逻辑复杂的情况,对奇数取平方

def handle_time(item):
    return item * item
odd_list = [handle_time(i) for i in range(21) if i % 2 == 1]
print(odd_list)
[1, 9, 25, 49, 81, 121, 169, 225, 289, 361]

生成器表达式:可迭代

odd_list= ( i for i in range(21) if i % 2 == 1)
print(odd_list)
for item in odd_list:
    print(item)
<generator object <genexpr> at 0x0000020492C46C80>
1
3
5
7
9
11
13
15
17
19

字典推导式

比如反转字典key,value的位置

my_dict = {"bobby1":22,"bobby2":23,"imooc":5}
reversed_dict = {value:key for key,value in my_dict.items()}
print(reversed_dict)
{22: ‘bobby1‘, 23: ‘bobby2‘, 5: ‘imooc‘}

集合推导式

my_dict = {"bobby1":22,"bobby2":23,"imooc":5}
my_set = {key for key,value in my_dict.items()}
print(my_set)
{‘bobby2‘, ‘bobby1‘, ‘imooc‘}

第五章 自定义序列类

标签:coroutine   metaclass   概念   require   expr   Staff   列表操作   声明   而且   

原文地址:https://www.cnblogs.com/wry789/p/13344804.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!