Python基礎之序列構成的數組

導語:本文章記錄了本人在學習Python基礎之數據結構篇的重點知識及我的心得,以加深本身的理解。

本文重點:html

一、瞭解列表、元組、字節序列、數組等數據結構;
二、瞭解上述數據結構相對應的迭代、切片、排序、拼接操做;
三、若是想把代碼寫的Pythonic,在保證代碼可讀性的前提下,代碼行數越少越好。

1、內置序列類型概覽

  • 容器序列:list、tuple、collections.deque
  • 扁平序列:str、bytes、bytearray、memoryview、array.array
  • 可變序列:list、bytearray、array.array、collections.deque、memoryview
  • 不可變序列:tuple、str、bytes

容器序列可以存放不一樣類型的數據,比扁平序列更靈活;
扁平序列只能存放一種類型的原子性的數據,體積更小速度更快。eg:數字,字符字節python

2、列表推導(list comprehension)和生成器表達式(generator expression)

一、列表推導:在[]中使用命令語句加for甚至if實現迭代推導出新列表的操做。
列表推導運用得當將使得代碼清晰優雅。
Python3中不存在Python2的列表推導變量泄漏問題,即列表推導中的局部變量與主程序的同名變量引用衝突問題。算法

eg1:利用ord()把單詞apple的ASCII碼以列表存起來。express

list_a=[ord(x)for x in "apple"]#ord()是返回字符對應ASCII碼的函數。
print(list_a)#輸出:[97, 112, 112, 108, 101]

eg2:使用列表嵌套循環求笛卡爾積。數組

Clothes=["T-shirt","sweater"]
Color=["red","yellow","blue"]
Product=[(x,y)for x in Clothes for y in Color]#嵌套循環
print(Product)#輸出:[('T-shirt', 'red'), ('T-shirt', 'yellow'), ('T-shirt', 'blue'), ('sweater', 'red'), ('sweater', 'yellow'), ('sweater', 'blue')]

句法提示:Python會忽略[ ],{ },( )中的換行符" ",把括號內的多行內容視爲一行。所以可在列表、列表推導、字典、生成器表達式中省略換行符。安全

二、學習生成器表達式應先了解的概念:
迭代器協議:對象必須提供next方法,執行該方法會返回迭代中的下一項,直到引發stopiteration中止迭代。
可迭代對象:實現迭代器協議的對象(實現方法:對象內部定義__iter__方法)
任何可迭代對象均可以使用for循環。由此看出Python的for循環抽象程度高於Java。數據結構

使用Iterable判斷一個對象是否可迭代:app

from collections import Iterable
print(isinstance("apple", Iterable))#True可迭代
print(isinstance(100, Iterable))#False不可迭代

三、生成器表達式按需返回一個結果對象,而非先構建一個完整的列表。
表達方法:語句格式與列表推導相似,但須在圓括號()中書寫。
特色:按需產值,延遲計算,節省內存,在生成大規模序列類型具有優點。dom

eg1:用生成器表達式初始化元組函數

tuple_a=(ord(x)for x in "apple")
for i in tuple_a:
    print(i)

eg2:用生成器表達式初始化數列

from array import array
ascii=array('I',(ord(x)for x in "apple"))
for i in ascii:
    print(i)

eg3:用生成器表達式計算笛卡爾積

Clothes=["T-shirt","sweater"]
Color=["red","yellow","blue"]
tuple_Product=((x,y)for x in Clothes for y in Color)
for i in tuple_Product:#生成器表達式是按需產值,沒法用print直接一次性輸出,須要配合for迭代輸出
    print(i)

3、元組功能

元組有兩重功能,一是看成記錄來用的數據模型,二是充當不可變的列表,

一、用做記錄:

元組用做記錄時,能夠理解爲數據+位置。之因此提位置是由於有序的位置賦予了數據獨特的意義。若是此時對元組進行排序等打亂位置的操做,會使得元組丟失本來所攜帶的信息。

Traveler_ids=[("hoya","85236669"),("Dennis","O7856P77"),("Sky","L336636")]
for i in Traveler_ids:
    print("%s/%s"%i)#平行賦值,元組拆包
#輸出:
hoya/85236669
Dennis/O7856P77
Sky/L336636
for name,_ in Traveler_ids:#對於拆包中不感興趣的一類元素能夠用佔位符「_"
    print(name)
#輸出:
hoya
Dennis
Sky

二、元組拆包:從元組中按位置順序提取元素。

元組拆包能夠應用到任何可迭代對象上,惟一的硬性要求是,被可迭代對象中的元素數量必須與接受這些元素的元組的空擋數一致。

t=(9,4)
print(divmod(*t))#利用*能夠把一個可迭代對象拆開做爲函數的參數,輸出爲(2, 1)

a=3
b=5
a,b=b,a#不使用中間變量實現值的交換
print(a,b)

用*來處理剩下的元素:
星號*能夠把不肯定參數裝到一塊兒。注意在平行賦值中,星號*只能用在一個變量名前面,可是這個變量能夠出如今賦值表達式的任意位置。

a,b,*rest=range(5)
print(a,b,rest)#輸出爲:0 1 [2, 3, 4]

a,*rest,b=range(5)
print(a,rest,b)#輸出爲0 [1, 2, 3] 4

三、嵌套元組拆包要求:接受元組的嵌套結構符合表達式自己的嵌套結構

format函數輸出通常格式:.
通常格式[fill,align,sign,0,width,.precision,type],每一處都是可選的.

  • fill,冒號+填充字符。填充字符可選,用於填充空白,默認爲空格;
  • align,對齊方式.<,>,^分別表明左,右,居中對齊,默認爲右對齊;
  • sign,取值爲:
  • +,全部數字簽名都要加上符號;
  • -,默認值,只在負數簽名加符號;
  • 空格,在正數前面加上一個空格;
  • 0,在寬度前面加0表示用0來填充數值前面的空白;
  • width,寬度;
  • .precision,精度的位數(注意前面有小數點".")
  • type,數據類型,如d(整數),s(字符串)等

format函數字符串映射方式:

  • 經過位置
print("{1}|{2}|{1}".format("a","b","c"))#經過位置填充。輸出:b|c|b
print("{}|{}|{}".format("a","b","c"))#也能夠不填寫位置索引,默認按照順序填充。輸出:a|b|c
  • 經過關鍵字
print("{Greeting},My name is {name}".format(Greeting="Hello",name="Hoya"))
#輸出:Hello,My name is Hoya
Fruit_number={"apple":15,"pear":10,"strawberry":20}
print("We have {x[apple]} apples,{x[pear]} pears,and {x[strawberry]} strawberries.".format(x=Fruit_number))#字典的key也能填充字符串。注意x是局部變量!
#輸出:We have 15 apples,10 pears,and 20 strawberries.
  • 經過下標
Friends=["Daniel","Dennis"]
print("I have two best friends,one is {x[0]},the other is {x[1]}".format(x=Friends))
#輸出:I have two best friends,one is Daniel,the other is Dennis

本質上是位置索引,由於傳入的對象非具體元素而是列表,故用下標填充。

  • 經過對象屬性
class Students:
    def __init__(self,x,y):
        self.x=x
        self.y=y
    def __str__(self):
        return "Hello,My name is {self.x},I come from {self.y}".format(self=self)
        
print(Students("Hoya","China"))#傳入類,把類的屬性值填充到字符串中format&嵌套元組實例綜合運用:

cities=[("New York","USA",(40.8086,-74.0204)),
("Mexico City","Mexico",(19.4333,-99.1333)),
("Tokyo","Japan",(35.6899,169.3254)),
("Sao Paul","USA",(-23.5478,-46.6358))]

print("{:15}|{:^9}|{:^9}".format("","lat","long"))
x="{:15}|{:^9.2f}|{:^9.2f}"#對經緯度小數點後保留兩位數字

for city,_,(lat,long) in cities :#平行賦值,對應結構一致。
    if long < 0:
        print(x.format(city,lat,long))#輸出以下:
               |   lat |  long   
New York       | 40.80 |-74.02 
Mexico City    | 19.43 |-99.13 
Sao Paul       |-23.54 |-46.63

namedtuple:具名元組,來自標準庫中的collections模塊中的方法。它能夠構建一個帶字段名的元組和一個有名字的類。
特色:可以直接使用名字訪問元素。

注意:

  1. 建立namedtuple須要兩個參數,第一個參數是類名,二是類字段的名字。後者能夠是數個字符串組成的可迭代對象,或者由空格分開的字段名組成的字符串。
  2. 存儲到類字段的數據要以一串參數的形式傳入到構造函數中。
  3. 能夠經過字段名或者位置讀取字段信息。
  4. 與tuple相同,namedtuple屬性不可變!

namedtuple屬性與方法:

  • _fields類屬性:返回這個類包含全部字段的元組
  • _make(iterable)類方法:接受一個可迭代對象來生成這個類的實例
  • _asdict()實例方法:以collections.OrderedDict的形式返回一個有序詞典

應用實例:

from collections import namedtuple
Star=namedtuple("Star", "name country coordinates age")
hoya=Star("Hoya","China",(20,30),18)
print(hoya.name)#讀取信息的兩種方式
print(hoya[0])

print(Star._fields)
Jack_data=("Jack","Russia",(100,36),14)
# Jack=Star(*Jack_data)#此方法與_make(iterable)類方法效果相同
Jack=Star._make(Jack_data)
print(Jack.age)
print(Jack._asdict())

for key,value in Jack._asdict().items():#字典內置方法items()而非item()
    print(key+":",value)#加號先後字符類型一致

四、用做不可變列表:

元組除了不支持增減,逆序,就地修改等直接或間接改變元素信息及位置的方法以外,支持列表其餘的全部方法。
因爲方法過多不便展現,詳情參考Fluent Python P27以及Python基礎教程筆記列表方法。

4、切片

一、切片和區間忽略最後一個元素的緣由:

  • Python和C以0做爲起始下標。當只有最後一個位置信息時,能夠快速看出切片中有幾個元素。eg:range(3)有3個元素
  • 已知切片區間的起止位置後,經過stop-start能快速計算出切片和區間的長度。
  • 如此能夠利用任意一個下表把序列分割成不重疊的兩部分。eg:list[:x]和list_2[x:]

二、多維切片和省略

  • 多維切片:對一維切片推廣到多維。在numpy中會用到多維切片。eg:a[m:n,u:v]
  • 省略:切片規範的一部分。eg:x[m,...]等價於x[m, :, :, :]

三、切片賦值

切片通常格式s[a:b:c],c取值爲負意味着反向取值。
注意:若是賦值對象是切片,賦值號另外一端的對象也必須是可迭代對象。即便單獨一個值,也要把它轉換成可迭代的序列。

反例:

list1=[1,3,6,9,10]
list1[1:3]=2#正確形式:list1[1:3]=[2]
Traceback (most recent call last):
  File "F:\xuexi\Fluent Python\section 2-4.py", line 3, in <module>
    list1[1:3]=2
TypeError: can only assign an iterable#切片右端必須是可迭代對象。

5、其它數據結構

列表儘管具備靈活簡單的特色,但並不能適用於各類需求,爲此咱們要尋找更好的選擇。下面介紹三種在某些狀況下能夠替換列表的數據類型。

一、數組:

若是咱們須要一個只包含數字的列表,那麼array.array比list更高效。

構造數組的通常格式:array(typecode,[ initializer])
typecode指數組類型,經常使用的以下:
initializer相似列表推導

Type code C Type Python Type Minimum size in bytes
'b' signed char int 1  
'B' unsigned char int 1  
'i' signed int int 2  
'I' unsigned int int 2  
'l' signed long int 4  
'L' unsigned long int 4  
'f' float float 4  
'd' double float 8

數組方法:
數組支持全部跟可變序列有關的操做,可參考Fluent Python P42以及array的Python官方library.
數組從Python3.4開始不支持諸如list.sort()這種就地排序的方法。
另外數組提供讀寫文件更快的方法,如.frombytes和.tofile

eg:綜合運用

from random import random
from array import array
array1=array("d",(random()for x in range(10^5)))#構建數組
fp=open("array1.bin","wb")#以二進制形式存儲文件
array1.tofile(fp)#打開文檔導入數據
fp.close()#關閉文檔
print(array1[-1])

array2=array("d")
fp=open("array1.bin","rb")
array2.fromfile(fp, 10^5)
fp.close()
print(array2[-1])
print(array1==array2)#輸出True,說明存取無誤。

二、雙向隊列

collections.deque類(雙向隊列)是一個線程安全、能夠快速從兩端添加或者刪除的數據類型。
deque構造:deque([initializer],maxlen=some_number)#傳入初值和maxlen參數。初值可在中括號中,也可採起相似列表推導的方法;maxlen限制deque的元素數量,選填。
deque方法


  • extend() 一次性從右端添加多個元素
    append() 從右端添加一個元素
    extendleft() 從左端添加多個元素,注意是逆序輸入(由於是逐個迭代插入的關係)
    appendleft() 從左端添加一個元素

  • pop() 從右端移除元素
    popleft() 從左端移除元素
    注意,deque是線程安全的,因此能夠在不一樣的線程中同時從兩端移除元素。
  • 旋轉與統計
    rotate(n)
    當參數爲正整數n時,rotate()將向右移動n位,並將隊列右端的n個元素移到左端,當參數爲負數-n是,rotate()將向左移動n位,並將隊列左邊的n個元素移動到右邊。
    count(e):統計隊列中某個元素的個數。
    To learn more:參考 Python標準library和Fluent Python P48

eg:

from collections import deque
deque1=deque([x*x for x in range(3)],maxlen=5)
print(deque1)#deque([0, 1, 4], maxlen=5)

deque1.extendleft([9,16,25])#隊列滿元素後,一端添加的元素會使得另外一端刪除同等數量的元素保持maxlen不變
print(deque1)#deque([25, 16, 9, 0, 1], maxlen=5)

deque1.rotate(-3)
print(deque1)#deque([0, 1, 25, 16, 9], maxlen=5)

deque1.popleft()
print(deque1)#deque([1, 25, 16, 9], maxlen=5)

deque實現了大部分列表所擁有的方法,具備靈活多用和線程安全的特色;但deque從中間刪除元素的操做會慢一些。
注:deque不支持切片操做(我的實踐經驗)

三、內存視圖

memoryview實際上是泛化和去數學化的NumPy數組。
它讓你在不須要複製內容的前提下在數據結構之間共享內存;數據結構能夠是任何形式。

6、其它知識

一、初始化嵌套列表

list1=[["_"]*3 for x in range(3)]
list2=[["_"]3]3
list11=1
list21=1
print(list1)#[['_', '_', '_'], ['_', 1, '_'], ['_', '_', '_']]
print(list2)#[['_', 1, '_'], ['_', 1, '_'], ['_', 1, '_']]
上文代碼是兩種初始化嵌套列表的方式,仔細觀察發現list2的賦值後在3個子列表中均有賦值,這是錯誤的初始化方法。緣由在於list2初始化的子列表引用一致,這種列表每每不是咱們想要的結果。
教訓:a*n語句中,若是序列a的裏的元素是對其餘可變對象的引用,就須要額外小心。緣由是會產生指向同一個可變對象的屢次引用!

二、元組嵌套列表的兩點問題

  • 不要把可變對象放到列表中
  • 增量賦值不是原子操做

原子操做:不會被線程調度機制打斷的操做,一旦執行將運行到結束。

三、list.sort和sorted

  • list.sort是就地排序,返回None。返回none的緣由是提示你此方法不會新建列表,讓調用者知道傳入的參數發生了改動,這實際上是Python的一個慣例。
  • sorted與之相反。它接受任何形式的可迭代對象做爲參數,返回一個列表。
  • 二者都有兩個參數供選擇。一是reverse,默認False升序排列,設定爲True會降序輸出;二是key,設置比較的關鍵字參數。

eg:

cities=["New York","Mexico City","Tokyo","Sao Paul"]
print(sorted(cities,key=len,reverse=True))#['Mexico City', 'New York', 'Sao Paul', 'Tokyo']
cities.sort(reverse=True)
print(cities)#['Tokyo', 'Sao Paul', 'New York', 'Mexico City']

sorted通常格式:

sorted(iterable,[,key[,reverse=True]]])

eg:

students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 13)]
print(sorted(students, key=lambda s: s[0]))#lambda是匿名函數;s(0)表明把可迭代對象中待排序元素中的第一個元素取出比較排序,這裏是諸如"john"的英文字符串。
#輸出:[('dave', 'B', 13), ('jane', 'B', 12), ('john', 'A', 15)]

四、bisect模塊:利用二分查找算法管理已排序的序列

bisect.bisect(haystack,needle):返回一個在haystack中插入needle保持序列升序的位置。
bisect.insort(seq,item):返回一個在seq中插入item保持序列升序的新序列。
bisect和insort同樣,能夠額外填寫lo和hi兩個參數控制

To learn more:https://docs.python.org/3/lib...

eg:

import bisect
list3=[1,3,5,7,9]
s=8
print(bisect.bisect(list3,s))#輸出:4
bisect.insort(list3,s)
print(list3)#輸出:[1, 3, 5, 7, 8, 9]
相關文章
相關標籤/搜索