一、Python 標準庫的序列類型分為:
容器序列:
能夠存放不同類型數據的序列(list、tuple、collections.deque)。
扁平序列:
只能容納一種類型的數據(str、bytes、bytearray 和 array.array)。
其中,容器序列存放的是它們所包含的任意類型的對象的引用,而扁平序列存放的是值而不是引用。換句話說,扁平序列是一段連續的內存空間,更加緊湊,但其內只能存放如數值、字符和字節等基礎類型。
二、而依據序列能否被修改,還能分為:
可變序列:
list、bytearray、array.array、collections.deque等。
不可變序列:
tuple、str和bytes。
序列可不可變指的是序列的大小與其存儲的值可不可變。如元組,雖然不可變,但其可變對象元素是可變的,因元組里存儲的“值”是引用,即該元素指向哪個對象是不可變的,但指向的對象本身是允許改變的。
文章目錄
一、列表推導與生成器表達式
1.列表推導
2.生成器表達式
3.區別
二、元組
1.元組拆包
2.處理多余值
3.嵌套元組拆包
4.具名元組
三、切片
1.多維切片
2.切片賦值
四、對序列使用+和*
1.增量賦值
五、序列排序
一、列表推導與生成器表達式
列表推導用于便捷地構建列表,生成器表達式可用于構建任何類型的序列。
1.列表推導
基本形式:[ obj for x1 in iter1 for x2 in iter2 for .... ],其中obj為使用x1、x2、…表示的對象,可以為元組、字典或可調用對象等,iter1、iter2、…為可迭代對象,用于產出值。最終的結果為由obj對象實例構成的數組。
列表推導只用于生成列表,若要構建其他類型的序列,則需要使用生成器表達式。
2.生成器表達式
生成器表達式可用于構建任意類型的序列(將生成器表達式傳入對應的構造函數),生成器表達式的語法跟列表推導差不多,只不過將[]換為()。
dict((x,y) for x in range(3) for y in "ABC")
>> {1: 'C', 2: 'C', 3: 'C'}
且若生成器表達式為一個函數調用中的唯一參數,則不需要再額外使用括號。
3.區別
列表推導會一次性生成含有 n 個元素列表,受限于內存,列表推導存在內存不足的風險。
而生成器表達式只在每次for循環運行時才生成一個組合,省去運行時的內存開銷。故在初始化除列表之外的序列,使用生成器表達式能夠省去額外的內存占用。
句法提示:Python會忽略[]、{}、()中的換行,在其內可以省略續行符\。
二、元組
把元組用作記錄:元組其實是對數據的記錄,元組中元素的信息蘊含了一個字段的數據與該字段的位置。將元組當作字段的集合能夠充分利用元組的數量與位置信息。
如:
city,year,pop,chg,area = ("Tokyo",2003,32450,0.66,8014)
以上將元組用作一條數據的記錄,充分利用了元組的記錄數據與記錄位置功能。
1.元組拆包
應用:
平行賦值,將一個可迭代對象里的元素,一并賦值到由對應變量組成的元組;
字符串格式化時,一個占位符對應一個元組里的元素。
用例:
# 交換兩個變量的值
a,b = b,a
使用元組拆包可以將可迭代對象拆開作為函數的多個參數:
t = (2,3)
add(*t)
>> 5
也可以借助元組拆包從函數中返回多個值:
# func為返回長度為3的元組的函數
a,b,c = func()
2.處理多余值
元組拆包可以用于任何可迭代對象上,前提是平行賦值時對應的元素數量一致,除非用*來收集多余元素。其中*前綴只能用于一個變量名前,但是該變量可以出現在賦值表達式的任意位置。
a,b,*rest = (1,2,3,4,5,6,7)
# a=1, b=2, rest=[3,4,5,6,7]
a,*rest,b = (1,2,3,4,5,6,7)
# a=1, rest=[2,3,4,5,6], b=7
同時也可以使用占位符_(本質上也是一個變量)來處理拆包時不需要的數據。
3.嵌套元組拆包
接收表達式的元組可以是嵌套式的,只要這個接收元組的嵌套結構符合表達式的嵌套結構,Python 就可做出正確對應:
(a,(b,c),d)=(1,(2,3),4)
# a=1, b=2, c=3, d=4
4.具名元組
collections.namedtuple是一個工廠函數,其返回一個類并構建一個帶名字的元組:
from collections import namedtuple
City = namedtuple('city','name country population')
# 構建City類,該類的實例為一個名為city的元組,且元素中每個元素都有對應的名字
tokyo = City("Tokyo","JP","36,933")
tokyo
>> city("Tokyo","JP","36,933")
tokyo.country
>> "JP"
構建一個具名元組類需要兩個參數:一個是元組名,另一個是類的各個對應字段的名字。后者可以是數個字符串組成的可迭代對象,也可以是由空格分開的字段名組成的字符串。然后其返回一個類,該類用以構造具名元組。
獲取字段信息可以通過索引,也可以以屬性的形式通過字段名訪問。
具名元組獨有的屬性:
_fields類屬性:一個包含該類所有字段名稱的元組;
_make(iterable)類方法:接收可迭代對象生成類的實例,作用相當于City(*iterable);
_asdict()實例方法:將具名元組以collections.OrderdDict的形式返回。
namedtuple構建的類的實例所消耗的內存與元組一樣,因為字段名都被存在對應的類里(類屬性)。
三、切片
切片的基本形式:[a:b[:c]],表示在a和b之間以c為間隔取值,其中c可選,也可為負,若為負則表示反向取值。
而Python 中切片其實是一個slice切片對象的實例。類似[a:b:c]返回一個切片對象:slice(a,b,c)。
1.多維切片
[]里還可以使用逗號分開的索引或切片。要正確地處理這種關系,需要自定義特殊方法__geiitem__、__setitem__以元組的形式接收a[i,j]中的索引。即要得到a[i,j]的值,Python會調用a.__getitem__(i,j)。
a[i,j]!=a[i][j],即前者需要自定義特殊方法實現,后者是用來處理多維序列的。
多維索引與多維切片的做法主要是為了支持自定義拓展,標準庫中并沒有相關用法。
2.切片賦值
切片不止可以用于提取序列內容,對于可變序列,若將切片放在賦值語句左邊,或作為del的操作對象,就可以對序列進行就地修改操作。
示例:
l = list(range(10))
l[2:5] = [20,30]
l
>> [0,1,20,30,5,6,7,8,9]
del l[5:7]
l
>> [0,1,20,30,5,8,9]
如果賦值的對象是一個切片,則賦值語句的右側必須是個可迭代對象。即便是由單獨一個值,也要把它轉換成可迭代對象。
四、對序列使用+和*
對于序列可以使用+進行拼接、使用*進行重復,這兩個運算符是產生一個新的序列而不修改原有對象。
對a*n,若a含有對其他對象的引用,則需要特別注意,產生的結果中多個元素可能會共享引用。
1.增量賦值
增量賦值運算符+=與*=的表現取決于它們的第一個操作對象。 對于表達式a += b:
如果a實現了__iadd__(就地加法),則會調用這個方法;
若沒有__iadd__,則會調用__add__,此時表達式的效果等于a = a + b,首先運算a+b,再將結果賦值給a。
即表達式中變量名會不會被關聯到新對象取決于該類型有沒有實現__iadd__方法,對應到序列中就是可變序列與不可變序列。即對于內置序列類型而言,使用+=增量賦值雖然表現出來無差別,但實際上實現的方法不同。
同樣以上也適應于*=,但后者對應的是__imul__與__mul__。
對不可變序列進行重復增量拼接的話,效率會很低,因為每次都會產生新的對象,解釋器需要將原對象拷貝到新對象,再在新對象上進行追加操作。
五、序列排序
一般使用list.sort方法與內置函數sorted進行排序。這兩個方法都使用Timsort算法,該算法為穩定的算法。兩種的方法的區別為:
使用list.sort()方法對列表進行就地排序,返回None值,只能在列表示例上調用。
而使用內置函數sorted()會新建一個已排序列表作為返回值。這個方法可以接收任何形式的可迭代對象作參數,包括不可變對象和生成器;但不管接收何種參數,其始終返回一個列表。
而不管是list.sort()還是sorted(),都接收兩個參數:
reverse:若為True,則使用降序排序;默認為False;
key:一個只接收一個參數的函數,該函數會在序列的每個元素上調用,產生的結果作為排序算法比較的關鍵字。默認為恒等函數。
* 數組(array):
若我們需要一個只包含數字的列表,那么array.array將比list更高效,因為數組背后存的并不是float對象,而是數字的字節表述。數組支持所有可變序列有關的操作,還提供文件更快的I/O方法。