第二章(提煉) 序列構成的數組(一)

一. 內置序列類型

        Python標準庫用C實現了豐富的序列類型:

  • 容器序列:list、tuple和collections.deque這些序列能存放不同類型的數據;
  • 扁平序列:str、bytes、bytearray、memoryview和array.array,這類序列只能容納一種類型;

        容器序列存放的是它們所包含的任意類型對象的引用,而扁平序列存放的是值而不是引用。換句話說,扁平序列是一段連續的內存空間。由此可見扁平序列其實更加緊湊,但是它裏面只能存放諸如字符、字節和數值這種基礎類型。

        序列類型還能按照能否被修改來分類:

  • 可變序列:list、bytearray、array.array、collections.deque和memoryview;
  • 不可變序列:tuple、str、bytes

        下圖展示了可變序列(MultableSequence)和不可變序列(Sequence)的差異(注:python內置的序列類型並不是直接從Sequence和MultableSequence這兩個抽象基類(ABC)集成而來的):

最基礎也是最重要的序列類型該是list了,list是一個可變的容器序列,下面我們開始學習列表推導和生成器表達式。

二. 列表推導和生成器表達式

         列表推導是構建list的快捷方式,而生成器表達式則可以用來創建其它任何類型的序列。

2.1 列表推導

演示1  把一個字符串變成Unicode碼位的列表

使用列表推導,上述代碼將變得十分簡潔:

        通常,我們只用列表推導來創建新的列表,並且儘量保持簡潔。如果列表推導超過了2行,可以考慮是不是用for循環代替。注:python會忽略代碼裏的[]、{}和()中的換行,因此如果代碼裏有多行的列表、列表推導、生成器表達式、字典這一類的,可以省略掉換行符\。

演示2  python3中列表推導不再有變量泄露的問題

        列表推導、生成器表達式,以及集合推導和字典推導,在python3中都有了自己的局部作用域,就像函數似的。表達式內部的變量和賦值只在局部起作用,表達式的上下文裏的同名變量可以被正常引用,局部變量並不會影響到它們。

2.2 列表推導同filter和map的比較

        列表推導可以幫助我們把一個一個序列或是其它可迭代類型中的元素過濾或是加工,然後再新建一個列表。Python內置的filter和map函數組合起來也能達到這一效果,但是性能上打了不小的折扣。

演示3  用列表推導和map/filter組合來創建相同的list

 2.3 笛卡爾積

        笛卡爾積是一個列表,列表裏的元素是由輸入的可迭代類型的元素對組成的元組,因此笛卡爾積列表的長度等於輸入變量長度的乘積。

演示4  使用列表推導計算笛卡爾積

2.4 生成器表達式

        雖然可以用列表推導來初始化元組、數組或其它序列類型,但是生成器表達式是最好的選擇。這是因爲生成器表達式背後遵循了迭代器協議,可以逐個產出元素,而不是先建立一個完整的列表,然後再把這個列表傳遞到某個構造函數裏。前者顯然能夠節省內存。

演示5  用生成器表達式初始化元組和數組

注:如果生成器表達式是一個函數調用中唯一的參數,那麼不需要使用額外的括號把它括起來。array的構造方法需要2個參數,因此括號是必須的;array構造方法的第一個參數指定了數組中數字的存儲方式。

演示6  使用生成器表達式計算笛卡爾積

        用到生成器表達式之後,內存裏不會留下一個有6個組合的列表,因爲生成器表達式會在每次for循環運行時才生成一個組合。如果要計算1000個元素的列表的笛卡爾積,生成器表達式就能幫忙省略掉for循環的開銷,即一個含有100萬個元素的列表。

注:原書第十四章專門講到生成器的工作原理。這裏只是簡單瞭解用生成器來初始化列表之外的序列,以及如果用它來避免額外的內存佔用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章