1. 程式人生 > >Python 3.7.1 基礎 資料型別 列表 元組 字串

Python 3.7.1 基礎 資料型別 列表 元組 字串

序列型別

1.前言

有三種基本序列型別:list(列表),tuples(元組)和 range (範圍物件)。專門用於處理二進位制資料和文字字串的附加序列型別 在專門章節中描述。

2.序列的通用操作

大多數序列型別都支援下表中的操作,包括可變和不可變的序列。python提供的collections.abc.Sequence使其更容易正確地執行自定義序列型別的操作。

此表列出了按升序排序的順序操作。在表中,s和t是相同型別的序列,n,i,j 和 k是整數,x是滿足強加的任何型別和值限制的任意s物件。

innot in操作符具有和他們相同的優先順序。+(串聯)和*(重複)操作具有相同的優先順序。

操作 結果 注意
x in s 如果s的某項等於x返回True,否則False (1)
x not in s 如果s的某項等於x返回False,否則True (1)
s + t s和 t的串聯 (6)(7)
s * n 或者 n * s n個s 拼接 (2)(7)
s[i] s的第i個元素,從0開始 (3)
s[i:j] s的切片,從i到j(不包括j,顧左不顧右) (3)(4)
s[i:j:k] s的切片,從i到j,步長為k (3)(5)
len(s) s的長度
min(s) s的最小元素
max(s) s的最大元素
s.index(x[, i[, j]]) x在s中第一次出現的位置(限定位置在i,j之間) (8)
s.count(x) x在s中出現的次數

相同型別的序列也支援比較。特別是,元組和列表通過比較相應的元素並且按字典順序比較。這意味著要比較相等,序列中的每個元素必須比較相等,並且兩個序列必須是相同型別並且具有相同的長度。(有關完整詳細資訊,請參閱語言參考中的比較。)

注意:
(1)雖然innot in操作僅用於一般情況下的簡單包含測試,但一些專門的序列(例如str,bytes 和 bytearray)也將它們用於子序列測試:

>>>
>>> "gg" in "eggs"
True

(2)小於0的n被視為0(其產生與s相同型別的空序列)。請注意,序列s 中的專案不會被複制; 它們被多次引用。這常常困擾著新的Python程式設計師; 考慮:

>>>
>>> lists = [[]] * 3
>>> lists
[[], [], []]
>>> lists[0].append(3)
>>> lists
[[3], [3], [3]]

事情是這樣的,[[]]是一個包含[]元素的非空列表,因此所有三個[[]]* 3元素都是對這個空列表元素的引用。修改任何lists元素都會修改此單個列表。您可以通過以下方式建立不同列表的列表:

>>>
>>> lists = [[] for i in range(3)]
>>> lists[0].append(3)
>>> lists[1].append(5)
>>> lists[2].append(7)
>>> lists
[[3], [5], [7]]

FAQ條目中提供了進一步的說明 如何建立多維列表?。

(3)如果i或j為負數,則索引相對於序列s的結尾: len(s) + ilen(s) + j被替換。但請注意,-0仍然是0

(4)從i到j的s的切片就是所有滿足k(i <= k < j)的s的索引項的拼接。如果i或j大於len(s),會使用len(s) 。如果i 被省略或使用None,從0開始。如果省略j或 None,使用len(s)。如果i大於或等於j,則切片為空。

def list_range_plus():
    s = [1,'a','bcd',223]
    print(s[0],s[1],s[2],s[3])

    print(s[-1],s[-2],s[-3],s[-4])
    print(s[len(s)-1], s[len(s)-2], s[len(s)-3], s[len(s)-4])

    print(s[0:-2],s[0:len(s)-2])
    print(s[-2:],s[len(s)-2:])# 從-2位置到結尾
    print(s[:-2], s[:len(s) - 2])
    print(s[::2])
# 輸出結果
1 a bcd 223
223 bcd a 1
223 bcd a 1
[1, 'a'] [1, 'a']
['bcd', 223] ['bcd', 223]
[1, 'a'] [1, 'a']
[1, 'bcd']

(5)基本內容同(4),只不過增加一個步長k。

(6)連線不可變序列總是會產生一個新物件。這意味著通過重複級聯構建序列將在總序列長度中具有二次執行時成本。要獲得線性執行時成本,您必須切換到以下替代方案之一:

  • 如果連線str物件,您可以構建一個列表並在最後使用 str.join(),或者寫入io.StringIO 例項並在完成時檢索其值。
  • 如果連線bytes物件,您可以類似地使用 bytes.join()或io.BytesIO,或者您可以與bytearray物件進行就地連線。 bytearray 物件是可變的並且具有有效的過度分配機制
  • 如果連線tuple物件,擴充套件list代替
  • 對於其他型別,請調查相關的類文件

譯者例項(幫助理解第6點的意思):

def list_connect():
    import string
    import time
    s = string.ascii_letters
    bases =''
    time1 = time.time()
    for i in range(100000):
        bases+=s
    #print(bases)
    end_time1 = time.time()
    print("普通連線5萬次字串花費時間:{:f}".format(end_time1-time1))
    time2 = time.time()
    list =[]
    for i in range(100000):
        list.append(s)
    #print("".join(list))
    end_time2 = time.time()
    print("list+join連線10萬次字串花費時間:{:f}".format(end_time2 - time2))
# 輸出結果
普通連線10萬次字串花費時間:5.403309
list+join連線10萬次字串花費時間:0.086005

可以看出第二種方法花費的時間快了70倍左右,而且記憶體消耗不會劇烈上升。 如果你的機器比較好,可以嘗試連線幾百萬次測試效能。

(7)某些序列型別(例如range)僅支援遵循特定模式的項序列,因此不支援序列連線或重複。

def list_range_plus():
    s = [1,'a','bcd',223]
    for i in range(3)+range(4):
        print(i)
# 輸出結果
Traceback (most recent call last):
File "xxx/list1.py", line 42, in list_test
    for i in range(3)+range(4):
TypeError: unsupported operand type(s) for +: 'range' and 'range'

(8)index方法在s中找不到x時會引發ValueError。並非所有實現都支援傳遞附加引數i和j。這些引數允許有效搜尋序列的子部分。傳遞額外的引數大致相當於使用s[i:j].index(x),只是沒有複製任何資料,並且返回的索引是相對於序列的開始而不是切片的開始。

3. 序列型別

3.1 可變序列型別

下表中的操作是在可變序列型別上定義的。

在表中,s是可變序列型別的例項,t是任何可迭代物件,x是滿足強加的任何型別和值限制的任意s物件(例如,bytearray只接受滿足值限制0 <= x <= 255的整數)。

操作 結果 注意
s[i] = x s的第i項被替換為 X,i從0開始
s[i:j] = t 從i到j的s切片 被可迭代t的內容替換
del s[i:j] s[i:j] = []一樣
s[i:j:k] = t s[i:j:k]中的元素被t取代 (1)
del s[i:j:k] 從列表中刪除s[i:j:k]元素
s.append(x) 將x附加到序列的末尾(與s[len(s):len(s)] = [x])相同
s.clear() 從s中刪除所有專案 (同於del s[:] (5)
s.copy() 建立s的淺表副本 (同於s[:] (5)
s.extend(t)s += t 使用t的內容擴充套件s(大致等於s[len(s):len(s)] = t
s *= n 更新s,其內容重複n次 (6)
s.insert(i, x) 在由i給出的索引處將x插入s (同於s[i:i] = [x]
s.pop([i]) 在i處檢索專案並將其從s中刪除 (2)
s.remove(x) 從s 中刪除第一個等於x的項s[i] (3)
s.reverse() 反轉s (4)

譯者例項

def seq_changelist():
    s = [1, 'a', 'bcd', 223]
    s1 =[ 'i','love','you']
    s[1:] = 'abc'
    print(s)
    s[1:] = ('abc', 'qian')
    print(s)
    s[1] = s1
    print(s)
    s[0] = 0
    print(s)
    del s[:1]
    print(s)
    s.append(' ')
    s.append(['-- by leng','.'])
    print(s)
    s.extend('2018')
    s.extend(['.12.','01'])
    print(s)
    s.insert(len(s),'!')
    print(s)
# 輸出結果
[1, 'a', 'b', 'c']
[1, 'abc', 'qian']
[1, ['i', 'love', 'you'], 'qian']
[0, ['i', 'love', 'you'], 'qian']
[['i', 'love', 'you'], 'qian']
[['i', 'love', 'you'], 'qian', ' ', ['-- by leng', '.']]
[['i', 'love', 'you'], 'qian', ' ', ['-- by leng', '.'], '2', '0', '1', '8', '.12.', '01']
[['i', 'love', 'you'], 'qian', ' ', ['-- by leng', '.'], '2', '0', '1', '8', '.12.', '01', '!']

注意:
(1)t必須與它所替換的切片具有相同的長度。

def seq_list_iport():
    import string
    s = [x for x in string.digits ]
    print(s)
    slice_len = len(s[::2])
    print(s[::2])
    s[::2]= "".join([ _ for _ in string.ascii_lowercase])[0:slice_len]
    print(s)
# 輸出結果
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
['0', '2', '4', '6', '8']
['a', '1', 'b', '3', 'c', '5', 'd', '7', 'e', '9']

(2)pop函式預設使用可選引數為-1,因此預設情況下會移除並返回最後一項。引數越界報錯IndexError

def list_pop():
	import string
    s = [x for x in string.digits]
    print(s)
    print("pop():", s.pop())
    print(s)
    print("pop(-1):", s.pop(-1))
    print(s)
    print("pop(4):", s.pop(4))
    print(s)
# 輸出結果
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
pop(): 9
['0', '1', '2', '3', '4', '5', '6', '7', '8']
pop(-1): 8
['0', '1', '2', '3', '4', '5', '6', '7']
pop(4): 4
['0', '1', '2', '3', '5', '6', '7']

(3)remove函式在s中找不到x的時候時候引發ValueError

def list_remove():
    import string
    s0 = ['!','@','*']
    s = ['a',123,45.24,[1,'d'],s0]
    print(s)
    print("remove('a')", s.remove('a'))
    print(s)
    print("remove([1,'d'])", s.remove([1,'d']))
    print(s)
    print("remove(s0)", s.remove(s0))
    print(s)
# 輸出結果
['a', 123, 45.24, [1, 'd'], ['!', '@', '*']]
remove('a') None
[123, 45.24, [1, 'd'], ['!', '@', '*']]
remove([1,'d']) None
[123, 45.24, ['!', '@', '*']]
remove(s0) None
[123, 45.24]

(4)reverse()當反轉大序列時,該方法修改了用於空間經濟性的序列。為了提醒使用者它是由副作用操作,它不會返回相反的順序。

def list_reverse():
    s0 = ['!', '@', '*']
    s = ['a', 123, 45.24, [1, 'd'], s0]
    print(s.reverse())
    print(s)
# 輸出結果
None
[['!', '@', '*'], [1, 'd'], 45.24, 123, 'a']

(5)clear()和copy()包括與不支援切片操作的可變容器的介面(例如dict和set)的一致性。

變化:版本3.3中增加的clear()copy()方法。

譯者注
list.copy()函式是一種淺copy,同copy.copy(),同s[:],不同於copy.deepcopy(),不同於同x=s=就是純引用,就是同一個物件)。
copy.copycopy.deepcopy的不同之處在於對拷貝物件中的可變元素(如list)和不可變的元素(如int,str)的處理。

  • 對於不可變元素的處理是一樣的,都只是複製一份,各不相干。
  • 對於可變元素,deepcopy還是複製一份,各不相干;copy是引用一份,你變我也變,我變你也變。

譯者例項

def list_copy(type=0):
    s0 = ['!', '@', '*']
    s = ['a', 123, 45.24, [1, 'd'], s0]
    new_s =[]
    if type == 0:
        new_s = s.copy()
    else:
        import copy
        if type == 1:
            new_s = copy.copy(s)
        elif type == 2:
            new_s = copy.deepcopy(s)
        elif type == 3:
            new_s = s
        elif type == 4:
            new_s = s[:]
    print("id(s)={}:{}".format(id(s),s))
    print("id(new_s):{}".format(id(new_s)))
    s[0],new_s[0] = 'an','bn'
    print(s)
    print(new_s)
    print(s)
    print(new_s)
    s[len(s)-1][0],new_s[len(new_s)-1][0] ='!!','**'
    print(s)
    print(new_s)
    print(s)
    print(new_s)
    s[3][0], new_s[3