天天看點

第四章:Python-進階程式設計-自定義序列類

第四章:Python-進階程式設計-自定義序列類

Python3進階核心技術97講 筆記

4.1 Python中的序列分類

4.1.1 容器序列

"""
list tuple deque 可以放入任意類型的資料
"""
           

4.1.2 扁平序列

"""
str bytes bytearray array.array
注意array與list的差別,array存放資料
類型需一緻。
"""
           

4.1.3 可變序列

"""
list deque bytearry array
"""
           

4.1.4 不可變

"""
str tuple bytes
"""
           

4.2 Python中序列類型的abc繼承關系

"""
需要知道的是,在Python中,其跟容器相關的資料結構
的抽象基類是放在collection.abc子產品下的
"""

"""
可變序列類型主要使用了
__setitem__
__delitem__
等魔法函數
"""

__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",
           ]


class Collection(Sized, Iterable, Container):

    __slots__ = ()

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

    
class Reversible(Iterable):

    __slots__ = ()

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

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Reversible:
            return _check_methods(C, "__reversed__", "__iter__")
        return NotImplemented
    
    
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]

    def index(self, value, start=0, stop=None):
        '''S.index(value, [start, [stop]]) -> integer -- return first index of value.
           Raises ValueError if the value is not present.

           Supporting start and stop arguments is optional, but
           recommended.
        '''
        if start is not None and start < 0:
            start = max(len(self) + start, 0)
        if stop is not None and stop < 0:
            stop += len(self)

        i = start
        while stop is None or i < stop:
            try:
                v = self[i]
                if v is value or v == value:
                    return i
            except IndexError:
                break
            i += 1
        raise ValueError

    def count(self, value):
        'S.count(value) -> integer -- return number of occurrences of value'
        return sum(1 for v in self if v is value or v == value)
    
    
 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

    @abstractmethod
    def insert(self, index, value):
        'S.insert(index, value) -- insert value before index'
        raise IndexError

    def append(self, value):
        'S.append(value) -- append value to the end of the sequence'
        self.insert(len(self), value)

    def clear(self):
        'S.clear() -> None -- remove all items from S'
        try:
            while True:
                self.pop()
        except IndexError:
            pass

    def reverse(self):
        'S.reverse() -- reverse *IN PLACE*'
        n = len(self)
        for i in range(n//2):
            self[i], self[n-i-1] = self[n-i-1], self[i]

    def extend(self, values):
        'S.extend(iterable) -- extend sequence by appending elements from the iterable'
        for v in values:
            self.append(v)

    def pop(self, index=-1):
        '''S.pop([index]) -> item -- remove and return item at index (default last).
           Raise IndexError if list is empty or index is out of range.
        '''
        v = self[index]
        del self[index]
        return v

    def remove(self, value):
        '''S.remove(value) -- remove first occurrence of value.
           Raise ValueError if the value is not present.
        '''
        del self[self.index(value)]

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

           

4.3 list中extend方法差別

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

a += [3, 4]
print(a)
a += (5, 6)  # 不報錯
print(a)
c = a + (7, 8)  # 報錯

"""
為什麼會有這種差别,
實際上在 += 時,Python
實作的魔法函數是 __iadd__
,調用這個魔法函數實際就是把
+= 右邊的值傳入 list 的 extend方法。
"""

"""
注意append的方法與extend的方法
append會将參數作為整個。extend會将
參數分開。
"""
           

4.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:]  # 切片開始位置大于清單長度時,傳回空清單

aList[len(aList):] = [9]  # 在清單尾部增加元素
aList[:0] = [1, 2]  # 在清單頭部插入元素
aList[3:3] = [4]  # 在清單中間位置插入元素
aList[:3] = [1, 2]  # 替換清單元素,等号兩邊的清單長度相等
aList[3:] = [4, 5, 6]  # 等号兩邊的清單長度也可以不相等
aList[::2] = [0] * 3  # 隔一個修改一個
print (aList)
aList[::2] = ['a', 'b', 'c']  # 隔一個修改一個
aList[::2] = [1,2]  # 左側切片不連續,等号兩邊清單長度必須相等
aList[:3] = []  # 删除清單中前3個元素

del aList[:3]  # 切片元素連續
del aList[::2]  # 切片元素不連續,隔一個删一個

           
"""
編寫支援切片的對象
"""


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

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

    def __getitem__(self, item):
        cls = type(self)
        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)
reversed(group)
for user in group:
    print(user)
    
           

4.5 什麼時候我們不該使用清單

# array, deque
# 數組


import array
# array和list的一個重要差別,array隻能存放指定的類型
my_array = array.array("i")
my_array.append(1)
my_array.append("abc")

           

4.6 清單推導式、生成器表達式、字典推導式

# 清單生成式(清單推導式)
# 提取出1-20之間的奇數

odd_list = []
for i in range(21):
    if i%2 == 1:
        odd_list.append(i)
        
odd_list = [i for i in range(21) if i%2 == 1]


# 邏輯複雜的情況
def handle_item(item):
    return item * item


odd_list = [handle_item(i) for i in range(21) if i%2 == 1]

# 清單生成式性能比清單操作高


# 生成器表達式
odd_gen = (i for i in range(21) if i%2 == 1)
print(type(odd_list))


# 字典推導式
my_dict = {"bobby1":22, "bobby2":23, "imooc.com":5}
reversed_dict = {value:key for key, value in my_dict.items()}


# 集合推導式
my_set = {key for key, value in my_dict.items()}
print(type(my_set))

my_set = set(my_dict.keys())  # 不夠靈活