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

元組除了用作“不可變的列表”,還可以用於記錄沒有字段名的記錄。

一. 元組不僅僅是不可變列表

1.1 元組和記錄

        元組其實是對數據的記錄:元組中的每個元素都存放了記錄中的一個字段,外加這個字段的位置。正是這個位置信息給數據賦予了意義。如果只把元組理解爲不可變列表,那其它信息——它所含有的元素的總數和它們的位置,似乎就變得可有可無。但是如果把元組當作一些字段的集合,那麼數量和位置信息就變得非常重要了。

演示1  把元組當作記錄

1.2 元組拆包

        元組拆包可以應用到任何可迭代對象上,唯一硬性的要求是,被迭代對象中的元素數量必須要跟接受這些元素的元組的空檔數一致。除非使用*來表示忽略多餘的元素。最好辨認的元組拆包形式就是平行賦值,也就是說把一個可迭代對象裏的元素,一併賦值到由對應的變量組成的元組中。

另外一個很優雅的寫法,當屬不使用中間變量交換2個變量的值:

還可以用*用算符把一個可迭代對象拆開作爲函數的參數:

下面的例子中,元組拆包的用法則是讓一個函數可以用元組的形式返回多個值,然後調用函數的代碼就能輕鬆地接受這些返回值。os.path.split()函數就會返回以路徑和最後一個文件名組成的元組:

在python中函數用*args來接收不確定數量的參數算是一種經典的寫法了。於是Python3裏,這個概念被擴展到了平行賦值中:

在平行賦值中,*前綴只能用在一個變量名的前面,但是這個變量可以出現在賦值表達式的任意位置:

1.3 嵌套元組拆包

        嵌套元組拆包是指接受表達式的元組可以是嵌套形式的。

演示2  利用嵌套元組來獲取經緯度

        元組已經設計的很好了,但作爲記錄來用的話,還是少了一個功能:我們時長會需要給記錄中的字段命名。 namedtuple函數的出現幫我們解決了這個問題。

1.4 具名元組

        collections.namedtuple是一個工廠函數,它可以用來構建一個帶字段名的元組和一個有名字的類。用namedtuple構建的類的實例所消耗的內存和元組是一樣的,因爲字段名都被存在對應的類裏面。這個實例跟普通的對象實例比起來也要小一些,因爲Python不會用__dict__來存放這些實例的屬性。

演示3  定義和使用具名元組

        創建一個具名元組需要2個參數,一個是類名,另一個是類的各個字段的名稱。後者可以是由數個字符串組成的可迭代對象,或者是由空格分隔的字段組成的字符串。 存放在對應字段裏面的數據要以一串參數的形式傳入構造函數中。具名元組允許通過字段名或位置信息來獲取一個字段的信息。

        除了從普通元組那裏繼承來的屬性外,具名元組還有一些自己專有的屬性。如:_fields類屬性、實例方法_asdict()。

演示4  具名元組的屬性和方法

        _fields屬性是一個包含這個類所有字段名稱的元組。用_asdict()把具名元組以collections.OrderedDict的形式返回,我們可以利用它來把元組裏的信息友好地呈現出來。現在我們已經知道了,元組是一種強大的可以當作記錄來用的數據類型。它的第二個角色是充當一個不可變的列表。

1.5 作爲不可變列表的元組

        除了跟增減元素相關的方法之外,元組支持列表的其它所有方法。還有一個例外,元組沒有__reversed__方法,但是這個方法只是個優化而已,reversed(obj)這個方法在沒有__reversed__的情況下也是合法的。

二. 切片

        在Python裏,像列表、元組和字符串這類序列類型都支持切片操作,但是實際上切片操作比人們所想象的要強大得多。

2.1 爲什麼切片和區間會忽略最後一個元素

        在切片和區間操作裏不包含區間範圍的最後一個元素是Python的風格:

  1. 當只有最後一個位置信息時,我們也可以快速看出切片和區間裏有幾個元素:range(3)和list[:3]都返回3個元素;
  2. 當起止位置信息都可見時,我們可以快速計算出切片和區間的長度:stop-start;
  3. 這樣作也讓我們可以利用任意一個下標來把序列分割成不重疊的2部分,只要寫成my_list[:x]和my_list[x:]就可以了。

2.2 對對象進行切片

演示5  使用s[a:b:c]的形式對s在a和b之間以c爲間隔取值。c的值可以爲負,負意味着反向取值:

        a:b:c這種用法只能作爲索引或者下標作用在[]中來返回一個切片對象:slice(a, b, c)。對seq[start:stop:step]進行求值的時候,Python會調用seq.__getitem__(slice(a, b, c))。(注:原書的第十章有對切片對象的詳細介紹。)

演示6 將純文本形式的收據以一行字符串的形式被解析

2.3 多維切片和省略

        []運算符還可以使用以逗號分開的多個索引或是切片,外部庫numpy就用到了這個特性,二維的numpy.ndarray就可以用a[i, j]的形式獲取值,抑或是用a[m:n, k:l]的方式來得到二維切片。要正確處理這種運算符的話,對象的特殊方法__getitem__和__setitem__需要以元組的形式來接收a[i, j]中的索引。即:如果要得到a[i, j]的值,Python會調用a.__getitem__((i, j))。Python內置的序列都是一維的,因此只支持單一索引。

        省略的具體寫法是三個英語句號(...),省略在Python解釋器眼裏是一個符號,而實際上它是Ellipsis對象的別名,而Ellipsis又是ellipsis類的單一實例(你沒看錯,ellipsis是類名,全小寫,而它的內置實例寫作Ellipsis。這跟bool是小寫,但是它的兩個實例寫作True/False是異曲同工)。它可以當作切片規範的一部分,也可以用在函數的參數清單中,比如f(a, ..., z)或a[i:...]。在numpy中,...用作多維數組切片的快捷方式。如x是四位數組,那麼x[i, ...]就是x[i, ;, ;, ;]的縮寫。

        Ellipsis或是多維索引的句法,只要是爲了支持用戶自定義類或者擴展,比如Numpy就是例子。切片除了用來提取序列裏的內容,還可以用來就地修改可變序列。

2.4 給切片賦值

演示7  如果把切片放在賦值語句的左邊,或把它作爲del操作的對象,我們就可以對序列進行嫁接、切除或就地修改操作。

        如果賦值的對象是一個切片,那麼賦值語句的右側必須是一個可迭代對象。即便只有單獨一個值,也要把它轉換成可迭代的序列。

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