目錄 | 上一節 (1.7 函式) | 下一節 (2.2 容器)
2.1 資料型別和資料結構
本節以元組和字典為代表介紹資料結構。
原始資料型別
Python 有一些原始資料型別:
- 整數
- 浮點數
- 字串(文字)
空型別
email_address = None
None
常用作可選值或缺失值的佔位符。它在條件語句中計算為 False
。
if email_address:
send_email(email_address, msg)
資料結構
實際的程式具有更復雜的資料。例如,關於股票的持有資訊:
100 shares of GOOG at $490.10
這是一個包含三個部分的“物件”:
- 股票的名稱或符號("GOOG",字串)
- 股份數目(100,整數)
- 價格(490.10,浮點數)
元組
元組是分組在一起的值的集合。
示例:
s = ('GOOG', 100, 490.1)
有時候會在語法上省略 ()
。
s = 'GOOG', 100, 490.1
特殊情況(0 元組,1 元組)。
t = () # An empty tuple
w = ('GOOG', ) # A 1-item tuple
元組一般用來表示簡單的記錄或結構。
通常,它是由多個部分組成的單個物件。這有一個很好的類比:元組就像資料庫表中的一行。
元組的內容是有序的(類似於陣列)。
s = ('GOOG', 100, 490.1)
name = s[0] # 'GOOG'
shares = s[1] # 100
price = s[2] # 490.1
但是,元組的內容無法修改。
>>> s[1] = 75
TypeError: object does not support item assignment
你可以基於當前元組建立一個新元組。
s = (s[0], 75, s[2])
元組打包
元組更多的是把相關的項打包到一個實體(entity)中。
s = ('GOOG', 100, 490.1)
然後,該元組很容易作為單個物件傳遞給程式的其它部分。
元組拆包
要在其它地方使用元組,可以把元組的各部分拆包為變數。
name, shares, price = s
print('Cost', shares * price)
左側變數的數目必須與元組的結構匹配。
name, shares = s # ERROR
Traceback (most recent call last):
...
ValueError: too many values to unpack
元組與列表
元組看起來像只讀列表。但是,元組最常用於由多個部分組成的單項。列表通常是型別相同的項的集合,
record = ('GOOG', 100, 490.1) # A tuple representing a record in a portfolio
symbols = [ 'GOOG', 'AAPL', 'IBM' ] # A List representing three stock symbols
字典
字典是鍵到值的對映。有時,字典也稱為雜湊表(hash table)或關聯陣列(associative array)。鍵用作訪問值的索引。
s = {
'name': 'GOOG',
'shares': 100,
'price': 490.1
}
常見操作
要從字典中獲取值,請使用鍵名。
>>> print(s['name'], s['shares'])
GOOG 100
>>> s['price']
490.10
>>>
要新增或修改值,請使用鍵名進行分配。
>>> s['shares'] = 75
>>> s['date'] = '6/6/2007'
>>>
要刪除值,請使用 del
語句。
>>> del s['date']
>>>
為什麼使用字典?
當存在很多不同的值並且可能會修改或操作這些值時,字典很有用。字典使程式碼更具可讀性。
s['price']
# vs
s[2]
練習
在上次的幾個練習中,編寫了一個取資料檔案 Data/portfolio.csv
的程式 。使用 csv
模組,可以輕鬆地逐行讀取檔案。
>>> import csv
>>> f = open('Data/portfolio.csv')
>>> rows = csv.reader(f)
>>> next(rows)
['name', 'shares', 'price']
>>> row = next(rows)
>>> row
['AA', '100', '32.20']
>>>
儘管讀取檔案很容易,但是與讀取資料相比,通常使用資料做更多的事情。例如,也許想儲存它並對其執行一些計算。不幸的是,原始的資料“行”並不能這樣做。例如,即使是簡單的數學計算也不行。
>>> row = ['AA', '100', '32.20']
>>> cost = row[1] * row[2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'str'
>>>
要執行更多的操作,通常需要以某種方式解釋原始資料,並將其轉換為更有用的物件型別,以便以後處理。有兩種簡單的方式可以選擇:元組或者字典。
練習 2.1:元組
在互動式提示符下,建立以下代表上一行的元組,但數字列要轉換為恰當的數字。
>>> t = (row[0], int(row[1]), float(row[2]))
>>> t
('AA', 100, 32.2)
>>>
使用這種方式,現在可以使用股份數目乘以價格來計算總價,
>>> cost = t[1] * t[2]
>>> cost
3220.0000000000005
>>>
在 Python 中,數學沒用了嗎?結果為什麼是 3220.0000000000005?
這是計算機上浮點硬體的產物,只能在二進位制(而不是十進位制)中準確表示小數。即使是涉及十進位制小數的簡單計算,也會引入小的誤差。這很正常,如果你之前沒有見過,可能會有點驚訝。
雖然在所有使用浮點小數的程式語言中都會發生這種情況,但是列印的時候可以把它隱藏,例如:
>>> print(f'{cost:0.2f}')
3220.00
>>>
元組是隻讀的。可以通過嘗試把股份數目改為 75 來驗證這點。
>>> t[1] = 75
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>
儘管無法更改元組的內容,但是始終可以建立一個全新的元組來替換舊的元組。
>>> t = (t[0], 75, t[2])
>>> t
('AA', 75, 32.2)
>>>
每當像這樣重新分配現有變數名時,舊值就會被丟棄。雖然上面的賦值可能看起來像在修改元組,但實際上是在建立一個新的元組,並且將舊的元組丟棄。
元組通常用於將值打包或拆包到變數中。請嘗試以下操作:
>>> name, shares, price = t
>>> name
'AA'
>>> shares
75
>>> price
32.2
>>>
取上面的變數並將其打包回元組中:
>>> t = (name, 2*shares, price)
>>> t
('AA', 150, 32.2)
>>>
練習 2.2:把字典當作資料結構
可以建立字典來替代元組。
>>> d = {
'name' : row[0],
'shares' : int(row[1]),
'price' : float(row[2])
}
>>> d
{'name': 'AA', 'shares': 100, 'price': 32.2 }
>>>
計算持有的總價:
>>> cost = d['shares'] * d['price']
>>> cost
3220.0000000000005
>>>
將此示例與上面涉及元組的相同的計算進行比較,將股份數目修改為 75。
>>> d['shares'] = 75
>>> d
{'name': 'AA', 'shares': 75, 'price': 32.2 }
>>>
與元組不同,字典可以自由修改。新增一些屬性:
>>> d['date'] = (6, 11, 2007)
>>> d['account'] = 12345
>>> d
{'name': 'AA', 'shares': 75, 'price':32.2, 'date': (6, 11, 2007), 'account': 12345}
>>>
練習 2.3: 字典的其它操作
如果將一個字典轉換為列表,則將獲得其所有的鍵:
>>> list(d)
['name', 'shares', 'price', 'date', 'account']
>>>
類似地,如果使用 for
語句對字典進行迭代,則將獲得其所有的鍵。
>>> for k in d:
print('k =', k)
k = name
k = shares
k = price
k = date
k = account
>>>
嘗試使用這個同時執行查詢的變體:
>>> for k in d:
print(k, '=', d[k])
name = AA
shares = 75
price = 32.2
date = (6, 11, 2007)
account = 12345
>>>
也可以使用 keys()
方法獲得所有的鍵:
>>> keys = d.keys()
>>> keys
dict_keys(['name', 'shares', 'price', 'date', 'account'])
>>>
在這裡,keys()
稍微有點不同,它返回的是一個 dict_keys
物件。
這是對原始字典的覆蓋,它始終提供當前字典的鍵——即使字典改變了。例如,試試一下操作:
>>> del d['account']
>>> keys
dict_keys(['name', 'shares', 'price', 'date'])
>>>
請注意,儘管沒有再次呼叫 d.keys()
,但鍵'account'
消失了。
一個更優雅地一起使用鍵和值的方式是使用 items()
方法。這可以獲得鍵值組成的元組 (key, value)
。
>>> items = d.items()
>>> items
dict_items([('name', 'AA'), ('shares', 75), ('price', 32.2), ('date', (6, 11, 2007))])
>>> for k, v in d.items():
print(k, '=', v)
name = AA
shares = 75
price = 32.2
date = (6, 11, 2007)
>>>
如果有類似於 items
的元組,那麼可以使用 dict()
函式建立一個字典。請嘗試以下操作:
>>> items
dict_items([('name', 'AA'), ('shares', 75), ('price', 32.2), ('date', (6, 11, 2007))])
>>> d = dict(items)
>>> d
{'name': 'AA', 'shares': 75, 'price':32.2, 'date': (6, 11, 2007)}
>>>