python高級(二)—— python內置序列類型

本文主要內容

  序列類型分類:html

    (1)容器序列、扁平序列python

    (2)可變序列、不可變序列git

  列表推導式github

  生成器表達式數組

  元組拆包app

  切片dom

  排序(list.sort方法和sorted函數)ide

  bisect函數

 

python高級——目錄post

  文中代碼均放在github上:https://github.com/ampeeg/cnblogs/tree/master/python高級

 

序列類型分類

 

   所謂序列,即元素有序排列,python標準庫用C實現了豐富的序列類型,按照序列中是否可存放不一樣類型的數據分爲"容器序列"和"扁平序列"。

  容器序列能夠存放通通類型的數據,而扁平序列只能存放一種類型      

    容器序列:list、tuple、collections.deque   
    扁平序列:str、bytes、bytearray、memoryview、array.array
  
  按照是否能修改的標準序列又可分爲"可變序列"和"不可變序列":
    可變序列:list、bytearrary、array.arrary、collections.deque和memoryview
    不可變序列:tuple、str和bytes

  因爲可變序列繼承自不可變序列,因此可變序列繼承的方法也較多,下面看看它們包含的方法:


方法名 不可變序列 可變序列
__contains__  有 有 
__iter__  有  有 
 __len__  有  有 
__getitem__   有  有 
__reversed__   有  有 
index   有  有 
count   有  有 
__setitem__    有 
__delitem__   有 
insert   有 
append   有 
reverse   有 
extend   有 
pop   有 
remove   有 
__iadd__    有 

  

  咱們以tuple和list類型爲例,對比源代碼中的方法,能夠明顯發現list的方法多於tuple:

  

 

列表推導式

# 列表推導式生成的是列表,會佔用系統內存
# 基本語法

list_1 = [x for x in range(1, 20)]
list_2 = [x ** 2 for x in range(1, 20)]


print(list_1)  # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
print(list_2)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361]

# 笛卡爾積型的列表推導式
list_3 = [(x, y) for x in range(1, 3)        # 1,2
                 for y in range(7, 10)]      # 七、八、9

                                             # 該表達式會先將1分別和七、八、9組合,而後再拿2和七、八、9組合,共6對
print(list_3)  # [(1, 7), (1, 8), (1, 9), (2, 7), (2, 8), (2, 9)]


list_4 = [x+y for x in range(1, 3)
                 for y in range(7, 10)]

print(list_4)   # [8, 9, 10, 9, 10, 11]

# 還能夠添加if語句
l = [1, 3, 4, 33, 45, 36, 422, 34, 67, 23, -4, -7, -345, 46, -6, -45, 32, -8, -4, 67, -4]

list_5 = [x for x in l if x > 0]   # 只取出大於0的生成列表
print(list_5)                      # [1, 3, 4, 33, 45, 36, 422, 34, 67, 23, 46, 32, 67]

 

生成器表達式

# 雖然列表推導式能夠用來初始化元組、數組或其餘序列類型,可是列表推導式會直接生成列表,佔用內存
# 而生成器遵照了迭代器協議,能夠逐個產出元素,而不是先創建一個完整的列表


# 生成器表達式直接將推導式的方括號換成圓括號便可

g = (x for x in range(1, 10000))

print(g)    # <generator object <genexpr> at 0x105c0efc0> :生成器對象


from collections import Iterable, Iterator

if isinstance(g, Iterable):
    print("iterable")          # 輸出iterable: 說明生成器g是可迭代的

if isinstance(g, Iterator):
    print("iterator")          # 輸出iterator:說明生成器g是迭代器

 

  下面咱們來對比一下列表推導式和生成器的效率

# 比較列表推導式和生成器
import time

start_time = time.time()
l = [x for x in range(1000000)]
print(time.time() - start_time)     # 0.1361069679260254

start_time = time.time()
g = (x for x in range(1000000))
print(time.time() - start_time)     # 1.1205673217773438e-05

# 可見,生成器遠快於推導式

 

元組拆包

# 咱們常常這樣給兩個變量同時賦值
a, b = 1, 2
print(a, b)     # 1 2

# 還能夠這樣
a, b = [1, 2]
print(a, b)     # 1 2

# 也能夠這樣
a, b = (1, 2)
print(a, b)     # 1 2

# 甚至能夠這樣
a, b = "ab"
print(a, b)     # a b

'''
    像以上這樣連續的賦值方式,右邊可使用逗號隔開;也能夠是序列。
    
    當拆包賦值的是序列時,python解釋器會先找該序列中的__iter__方法,若是該方法不存在,則尋找__getitem__方法。
       
    接下來講其餘用法
'''

# 賦值後優雅地交換兩個變量
a, b = (1, 2)
a, b = b, a
print(a, b)        # 2 1

# 使用*號來處理多餘的數據
a, b, *s = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(a, b, s)        # 1 2 [3, 4, 5, 6, 7, 8, 9]
                      # 這樣從第三個元素開始的全部值都賦給了s

a, b, *s = (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(a, b, s)        # 1 2 [3, 4, 5, 6, 7, 8, 9]
                      # 注意,原本是元組,賦以後的s變成了列表. 若是s爲空的話也會返回空列表

*s, a, b = (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(s, a, b)        # [1, 2, 3, 4, 5, 6, 7] 8 9
                      # *s也能夠放在前面

a, *s, b = (1, 2, 3, 4, 5, 6, 7, 8, 9)
print(a, s, b)        # 1 [2, 3, 4, 5, 6, 7, 8] 9
                      # *s也能夠放在中間

# 嵌套元組拆包
a, b, (c, d) = (1, 2, (3, 4))
print(a, b, c, d)     # 1 2 3 4
                      # 只要按照右邊的形式就可賦值

a, b, *c = (1, 2, (3, 4))
print(a, b, c)     # 1 2 [(3, 4)]

 

 1 ################################
 2 #
 3 # 如下的例子用以說明拆包賦值時,解釋器會按照__iter__、__getitem__的順序調用類中的方法
 4 #
 5 ################################
 6 class Foo:
 7     def __init__(self, s):
 8         self.s = s
 9 
10     def __iter__(self):
11         print("iter")
12         return iter(self.s)
13 
14     def __getitem__(self, item):
15         return self.s[item]
16 
17 if __name__ == "__main__":
18     foo = Foo("sdfafasfasf")
19     a, b, *s = foo
20     print(a, b)
拆包賦值的內部實現

 

  以前咱們經過源碼已經對比過list和tuple類中的方法和屬性,下面列出《流暢的python》整理的列表和元組的方法及屬性:

表 列表或元組的方法和屬性

  列  表 元  組 說  明
s.__add__(s2)
· · s1 + s2 , 拼接
s.__iadd__(s2) ·   s1 += s2,就地拼接
s.append(e) ·   在尾部添加一個新元素
s.clear() ·   刪除全部元素
s.__contains__(e) · · s是否包含e
s.copy() ·   列表的淺複製
s.count(e) · · e在s中出現的次數
s.__delitem__(p) ·   把位於p的元素刪除
s.extend(it) ·   把可迭代對象it追加給s
s.__getitem__(p) · · s[p],獲取位置p的元素
s.__getnewargs__()   · 在pickle中支持更加優化的序列化
s.index(e) · · 在s中找到元素e第一次出現的位置
x.insert(p,e) ·   在位置p以前拆入e
s.__iter__() · · 獲取s的迭代器
s.__len__() · · len(s),長度
s.__mul__(n) · · s * n,n個s的重複拼接
s.__imul__(n) ·   s *= n,就地城府拼接
s.__rmul__(n) · · n * s,反向拼接*
s.pop([p]) ·   刪除最後或者是位於p的元素,並返回它的值
s.remove(e) ·   刪除s中第一次出現的e
s.reverse() ·   就地把s的元素倒序排列
s.__reversed__() ·   返回s的倒序迭代器
s.__setitem__(p,e) ·   s[p]=e,把元素e放在位置p,替代已經在那個位置的元素
s.sort([key], [reverse]) ·   就地對s中的元素進行排序,可選的參數有key和是否倒序reverse

   

  說明:以上元組中不加黑點的不表明必定不能這樣使用,只是其做用和列表不一樣(說明裏面有解釋)。例如兩個元組a和b進行增量賦值a+=b也是能夠的,只是這個操做不是就地拼接,而是生成了新的元組。

切片

'''
    在python中,內置的序列類型都支持切片操做,切片操做的用法十分簡單:
    list[start: stop: step]    , 其中不包括區間範圍內最後一個(事實上這是python的風格,通常不包含區間最後一個)
    python裏面能使用切片操做是由於實現了__getitem__方法,切片時會給該方法傳遞slice(start: stop: step) 參數
'''

if __name__ == "__main__":
    # 基本操做
    l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    print(l[2:])     # 第3個元素到最後   :[3, 4, 5, 6, 7, 8, 9]
    print(l[:3])     # 第一個元素到最後   :[1, 2, 3]

    s = "abcdefghijklmn"
    print(s[2::2])   # 從第三個字母開始,隔一個字母取一個 : cegikm
    print(s[::-1])   # 倒序排列 : nmlkjihgfedcba
    print(s[::-2])   # 倒序隔一個取一個 nljhfdb
    print(s[-2::-2]) # 倒序第二隔開始,隔一個取一個

    # 利用切片賦值
    l[2:5] = [20, 30]
    print(l)         # [1, 2, 20, 30, 6, 7, 8, 9]
    try:
        l[2:5] = 40      # 報錯:TypeError: can only assign an iterable
                         # 利用切片賦值時傳入的必須是可迭代對象
    except Exception as e:
        print(e)         # can only assign an iterable
    l[2:5] = (40,)
    print(l)             # [1, 2, 40, 7, 8, 9]
    l[2:3] = "sajfljls"  # 字符串屬於序列,也能夠迭代
    print(l)             # [1, 2, 's', 'a', 'j', 'f', 'l', 'j', 'l', 's', 7, 8, 9]

 

排序(list.sort方法和sorted函數)

'''
    list.sort方法和sorted內置函數都有排序的功能,區別以下
        list.sort是就地排序列表,不會把原列表複製一份。該方法返回None,以提醒不會新建一個列表。
        sorted函數會新建一個列表做爲返回值,這個函數能夠接受任何可迭代對象,甚至包括不可變序列或生成器,最後返回的老是列表。

    list.sort和sorted都有兩個參數:
        reverse:默認爲False,設定爲True以降序排列
        key:一個只有一個參數的函數,這個函數會做用於序列的每個元素上,而後以該函數的結果做爲關鍵字排序

'''

if __name__ == "__main__":
    # 一、list.sort就地排序,而sorted返回列表
    l = [x for x in range(10, 0, -1)]      # 初始化一個列表:[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    print(id(l), l)    # l最初的地址:4536449800 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    l.sort()
    print(id(l), l)    # 排序後的地址:4536449800 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
                       # l先後的的地址沒變,說明是就地排序


    l = [x for x in range(10, 0, -1)]  # 初始化一個列表:[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    print(id(l), l)  # l最初的地址:4415318984 [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
    l = sorted(l)
    print(id(l), l)  # 排序後的地址:4415318792 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    # 二、sorted能夠接受任何可迭代對象
    l = (x for x in range(10, 0, -1))
    print(type(l))        # 迭代器 <class 'generator'>
    print(sorted(l))      # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    s = "qwertyuiopasdfghjklzxcvbnm"   # 字符串序列
    print(sorted(s))      # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

    s = (1, 3, 2, 456, 345, 12, 2, 5, 78, 34)   # 不可變元組
    print(sorted(s))      # [1, 2, 2, 3, 5, 12, 34, 78, 345, 456]

    # 三、reverse參數
    s = "qwertyuiopasdfghjklzxcvbnm"
    print(sorted(s, reverse=True))   # ['z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k', 'j', 'i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a']


    # 四、key參數
    s = "QwERTYuioPaSdfGHjKLzXcvbnm"
    print(sorted(s))    # ['E', 'G', 'H', 'K', 'L', 'P', 'Q', 'R', 'S', 'T', 'X', 'Y', 'a', 'b', 'c', 'd', 'f', 'i', 'j', 'm', 'n', 'o', 'u', 'v', 'w', 'z']
    print(sorted(s, key=str.lower))   # 忽略大小寫 ['a', 'b', 'c', 'd', 'E', 'f', 'G', 'H', 'i', 'j', 'K', 'L', 'm', 'n', 'o', 'P', 'Q', 'R', 'S', 'T', 'u', 'v', 'w', 'X', 'Y', 'z']
    print(sorted(s, key=str.upper))   # 也是忽略大小寫
##########################
#
#  如下自定義一個類也可以使用sorted函數
#
##########################

class Obj:
    def __init__(self):
        self.s = [x for x in range(10, 0, -1)]

    def __getitem__(self, item):
        print("getitem")
        return self.s[item]

    def __repr__(self):
        return str(self.s)

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

obj = Obj()
print(obj)           # [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

# 添加getitem後可使用sorted函數  (實驗時請注視掉getitem方法)
print(sorted(obj))   #  打印10次getitem   , [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 添加iter方法
print(sorted(obj))   # 此時解釋器會先調用iter方法,不會再使用getitem方法
                     # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
使自定義類也可以使用sorted函數調用

 

bisect

'''
    bisect模塊主要用來管理有順序的序列
    bisect模塊包含的主要函數是bisect和insort,兩個函數都使用二叉樹方法搜索
    一、bisect(haystack, needle)
        haystack必須是一個有序的序列,該函數搜索needle在haystack中的位置,該位置使得將needle插入後haystack仍然升序
        查找到位置後可用haystack.insert()插入

    二、insort(seq, item)
        把item插入到seq中,並能保持seq的升序

'''

#  本人認爲《流暢的python》中的對該模塊介紹的例子比較經典,故引用之

# 一、關於bisect.bisect的示例
import bisect
import sys

HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]

ROW_FMT = '{0:2d} @ {1:2d}    {2}{0:<2d}'

def demo(bisect_fn):
    for needle in reversed(NEEDLES):
        position = bisect_fn(HAYSTACK, needle)
        offset = position * '  |'
        print(ROW_FMT.format(needle, position, offset))


if __name__ == '__main__':

    if sys.argv[-1] == 'left':
        bisect_fn = bisect.bisect_left
    else:
        bisect_fn = bisect.bisect

    print('DEMO:', bisect_fn.__name__)
    print('haystack ->', ' '.join('%2d' % n for n in HAYSTACK))
    demo(bisect_fn)

''' 輸出以下 DEMO: bisect haystack -> 1 4 5 6 8 12 15 20 21 23 23 26 29 30 31 @ 14 | | | | | | | | | | | | | |31 30 @ 14 | | | | | | | | | | | | | |30 29 @ 13 | | | | | | | | | | | | |29 23 @ 11 | | | | | | | | | | |23 22 @ 9 | | | | | | | | |22 10 @ 5 | | | | |10 8 @ 5 | | | | |8 5 @ 3 | | |5 2 @ 1 |2 1 @ 1 |1 0 @ 0 0 ''' # 另,bisect.bisect函數有兩個可選參數——lo和hi來縮小搜索範圍,lo的默認值是0,hi的默認值是序列的長度 # 再另,bisect.bisect函數實際上是bisect_right函數的別名,還有一個bisect_left,插入位置若是有相等的元素時,插入元素會放在它相等的 # 元素後面,後者會放在前面 # 根據分數,查到等級 def grade(score, breakpoints=[60, 70, 80, 90], grades = 'FDCBA'): i = bisect.bisect(breakpoints, score) # 這裏的bisect.bisect實際上使用的是bisect_right return grades[i] print([grade(score) for score in [33, 55, 90, 87, 65, 78, 34, 60, 100]])
# 二、關於bisect.insort函數

import bisect
import random

SIZE = 7

random.seed(1729)

my_list = []
for i in range(SIZE):
    new_item = random.randrange(SIZE*2)
    bisect.insort(my_list, new_item)
    print('%2d ->' % new_item, my_list)
    
    '''輸出:
    10 -> [10]
     0 -> [0, 10]
     6 -> [0, 6, 10]
     8 -> [0, 6, 8, 10]
     7 -> [0, 6, 7, 8, 10]
     2 -> [0, 2, 6, 7, 8, 10]
    10 -> [0, 2, 6, 7, 8, 10, 10]
    '''
    
# 另,insort函數也有insort_left,背後使用的是bisect_left

 

python高級系列文章目錄

python高級——目錄

相關文章
相關標籤/搜索