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

来源:https://www.cnblogs.com/liao-sir/archive/2018/02/13/8432259.html
-Advertisement-
Play Games

本文主要內容 序列類型分類: (1)容器序列、扁平序列 (2)可變序列、不可變序列 列表推導式 生成器表達式 元組拆包 切片 排序(list.sort方法和sorted函數) bisect 文中代碼均放在github上:https://github.com/ampeeg/cnblogs/tree/m ...


本文主要內容

  序列類型分類:

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

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

  列表推導式

  生成器表達式

  元組拆包

  切片

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

  bisect

 

  文中代碼均放在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)]      # 7、8、9

                                             # 該表達式會先將1分別和7、8、9組合,然後再拿2和7、8、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__":
    # 1、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]

    # 2、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]

    # 3、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']


    # 4、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,兩個函數都使用二叉樹方法搜索
    1、bisect(haystack, needle)
        haystack必須是一個有序的序列,該函數搜索needle在haystack中的位置,該位置使得將needle插入後haystack仍然升序
        查找到位置後可用haystack.insert()插入

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

'''

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

# 1、關於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]])
# 2、關於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

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • #include #include using namespace std; const int N = 100001; vector school[N];//玩玩vector int sum[N]; int main() { int n,id,score; scanf("%d",&n); for(... ...
  • 1011. World Cup Betting (20) 時間限制 400 ms 記憶體限制 65536 kB 代碼長度限制 16000 B 判題程式 Standard 作者 CHEN, Yue 時間限制 400 ms 時間限制 400 ms 記憶體限制 65536 kB 記憶體限制 65536 kB 代 ...
  • 傳統的給文件鏈接添加版本號的方法是使用gulp-rev,這裡提出的解決方案是使用python來替代gulp-rev。 將以上代碼另存成auto_version.py文件後,新建auto_version.bat文件,寫入以下內容: 修改好.bat文件里的路徑後,雙擊運行即可。 ...
  • Android上不應該使用枚舉,占記憶體,應該使用@XXXDef註解來替代 使用 Enum 的缺點 每一個枚舉值都是一個對象,在使用它時會增加額外的記憶體消耗,所以枚舉相比與 Integer 和 String 會占用更多的記憶體。 較多的使用 Enum 會增加 DEX 文件的大小,會造成運行時更多的開銷, ...
  • #用for 迴圈for i in range(1,10): #print(i) # print(i*'*') for j in range(1,i+1): print('%d * %d = %d'%(j,i,i*j),end=' ') print('\n')#用while 迴圈 i = 1while ...
  • 什麼是Ajax Ajax(Asynchronous JavaScript and XML) 非同步JavaScript和XML Ajax實際上是下麵這幾種技術的融合: (1)XHTML和CSS的基於標準的表示技術 (2)DOM進行動態顯示和交互 (3)XML和XSLT進行數據交換和處理 (4)XMLH ...
  • Spring MVC的配置和使用 筆記倉庫: "https://github.com/nnngu/LearningNotes" Spring MVC需要的jar包 文章中 Spring MVC 使用的版本是 3.2.18 , 需要的 jar 包如下: 使用 Maven 構建的 Java 項目,需要在 ...
  • 正文之前 在學習了一段時間的Java Web的內容之後,當然需要有個項目來練練手,我相信大多數人的首選項目都是信息管理系統吧,所以我選擇了商品信息管理系統 目前項目源碼已全部上傳至GitHub,歡迎大家來 fork —— "商品信息管理系統" 正文 項目構思 簡易的管理系統,結構為 Servlet ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...