概述
通常,程式必須處理許多物件。
股票的投資組合股票價格表這裡有三種主要的選擇(譯註:資料結構)可以使用:
列表。有序的資料。字典。無序的資料。集合。互異且無序的資料。把列表當作容器當資料順序很重要時,請使用列表。記住,列表可以儲存任何型別的物件。例如,包含元組的列表:
portfolio = [ ('GOOG', 100, 490.1), ('IBM', 50, 91.3), ('CAT', 150, 83.44)]portfolio[0] # ('GOOG', 100, 490.1)portfolio[2] # ('CAT', 150, 83.44)
列表構建從零開始構建列表。
records = [] # Initial empty list# Use .append() to add more itemsrecords.append(('GOOG', 100, 490.10))records.append(('IBM', 50, 91.3))...
從檔案讀取記錄的示例:
records = [] # Initial empty listwith open('Data/portfolio.csv', 'rt') as f: next(f) # Skip header for line in f: row = line.split(',') records.append((row[0], int(row[1]), float(row[2])))
把字典當作容器
如果要快速隨機查詢(透過鍵名),那麼字典很有用。例如,股票價格字典:
prices = { 'GOOG': 513.25, 'CAT': 87.22, 'IBM': 93.37, 'MSFT': 44.12}
以下是一些簡單的查詢:
>>> prices['IBM']93.37>>> prices['GOOG']513.25>>>
字典構建
從零開始構建字典的示例:
prices = {} # Initial empty dict# Insert new itemsprices['GOOG'] = 513.25prices['CAT'] = 87.22prices['IBM'] = 93.37
從檔案內容填充字典的示例:
prices = {} # Initial empty dictwith open('Data/prices.csv', 'rt') as f: for line in f: row = line.split(',') prices[row[0]] = float(row[1])
注意:如果是在 Data/prices.csv 檔案上嘗試此操作,會發現幾乎可以正常工作——但是,在末尾有一個空行導致程式崩潰了。需要找出一些方法來修改程式碼以解決此問題(參見練習 2.6)。
字典查詢測試鍵是否存在:
if key in d: # YESelse: # NO
可以查詢可能不存在的值,並在值不存在的情況下提供預設值。
name = d.get(key, default)
示例:
>>> prices.get('IBM', 0.0)93.37>>> prices.get('SCOX', 0.0)0.0>>>
組合鍵
在 Python 中,幾乎任何型別的值都可以用作字典的鍵。字典的鍵必須是不可變型別。例如,元組:
holidays = { (1, 1) : 'New Years', (3, 14) : 'Pi day', (9, 13) : "Programmer's day",}
然後訪問:
>>> holidays[3, 14]'Pi day'>>>
列表,集合或者其它字典都不能用作字典的鍵,因為列表和字典(譯註:集合也是使用雜湊技術實現的)是可變的。
集合集合是互異且無序的資料。
tech_stocks = { 'IBM','AAPL','MSFT' }# Alternative syntaxtech_stocks = set(['IBM', 'AAPL', 'MSFT'])
集合對於成員關係測試很有用。
>>> tech_stocksset(['AAPL', 'IBM', 'MSFT'])>>> 'IBM' in tech_stocksTrue>>> 'FB' in tech_stocksFalse>>>
集合對於消除重複也很有用。
names = ['IBM', 'AAPL', 'GOOG', 'IBM', 'GOOG', 'YHOO']unique = set(names)# unique = set(['IBM', 'AAPL','GOOG','YHOO'])
其它集合操作:
names.add('CAT') # Add an itemnames.remove('YHOO') # Remove an items1 | s2 # Set unions1 & s2 # Set intersections1 - s2 # Set difference
練習在這些練習中,你開始構建的程式是本課程剩餘部分使用的主要程式之一。請在 Work/report.py 檔案中工作。
練習 2.4:包含元組的列表Data/portfolio.csv 檔案包含投資組合中的股票列表。在 練習 1.30 中,你編寫了一個讀取該檔案並執行簡單計算的 portfolio_cost(filename) 函式。
程式碼看起來應該像下面這樣:
# pcost.pyimport csvdef portfolio_cost(filename): '''Computes the total cost (shares*price) of a portfolio file''' total_cost = 0.0 with open(filename, 'rt') as f: rows = csv.reader(f) headers = next(rows) for row in rows: nshares = int(row[1]) price = float(row[2]) total_cost += nshares * price return total_cost
請使用這些程式碼作為指導,建立一個新檔案 report.py 。在 report.py 檔案中,定義 read_portfolio(filename) 函式,該函式開啟 Data/portfolio.csv 檔案並將其讀入到包含元組的列表中。為此,你需要對上面的程式碼做一些小修改。
首先,建立一個最初設為空列表的變數,而不是定義 total_cost = 0。例如:
portfolio = []
接著,把每一行準確地儲存到元組中(就像在上次的練習中做的那樣),然後把元組追加到列表中,而不是合計總的費用。
for row in rows: holding = (row[0], int(row[1]), float(row[2])) portfolio.append(holding)
最後,返回得到的portfolio 列表。
請互動式地試驗函式(提醒,要執行此操作,首先需要在直譯器執行 report.py 程式)。
提示:當在終端執行檔案的時候,請使用 -i 引數。
>>> portfolio = read_portfolio('Data/portfolio.csv')>>> portfolio[('AA', 100, 32.2), ('IBM', 50, 91.1), ('CAT', 150, 83.44), ('MSFT', 200, 51.23), ('GE', 95, 40.37), ('MSFT', 50, 65.1), ('IBM', 100, 70.44)]>>>>>> portfolio[0]('AA', 100, 32.2)>>> portfolio[1]('IBM', 50, 91.1)>>> portfolio[1][1]50>>> total = 0.0>>> for s in portfolio: total += s[1] * s[2]>>> print(total)44671.15>>>
建立的包含元組的列表非常類似於二維(2-D)陣列。例如,使用諸如 portfolio[row][column] ( row 和column 是整數)的查詢來訪問特定的列和行。
也就是說,可以使用像下面這樣的語句重寫最後的 for 迴圈:
>>> total = 0.0>>> for name, shares, price in portfolio: total += shares*price>>> print(total)44671.15>>>
練習 2.5:包含字典的列表
使用字典(而不是元組)修改在練習 2.4 中編寫的函式來表示投資組合中的股票。在字典中,使用欄位名 "name", "shares" 和 "price" 來表示輸入檔案中的不同列。
以與練習 2.4 中相同的方式試驗這個新的函式。
>>> portfolio = read_portfolio('Data/portfolio.csv')>>> portfolio[{'name': 'AA', 'shares': 100, 'price': 32.2}, {'name': 'IBM', 'shares': 50, 'price': 91.1}, {'name': 'CAT', 'shares': 150, 'price': 83.44}, {'name': 'MSFT', 'shares': 200, 'price': 51.23}, {'name': 'GE', 'shares': 95, 'price': 40.37}, {'name': 'MSFT', 'shares': 50, 'price': 65.1}, {'name': 'IBM', 'shares': 100, 'price': 70.44}]>>> portfolio[0]{'name': 'AA', 'shares': 100, 'price': 32.2}>>> portfolio[1]{'name': 'IBM', 'shares': 50, 'price': 91.1}>>> portfolio[1]['shares']50>>> total = 0.0>>> for s in portfolio: total += s['shares']*s['price']>>> print(total)44671.15>>>
在這裡可以看到,每個條目的不同欄位是透過鍵名來訪問的,而不是數字型別的列號。這通常是首選方式,因為這樣得到的程式碼在以後易於閱讀。
檢視大型的字典或者列表可能會很混亂。要使除錯的輸出變得整潔,可以考慮使用 pprint() 函式。
>>> from pprint import pprint>>> pprint(portfolio)[{'name': 'AA', 'price': 32.2, 'shares': 100}, {'name': 'IBM', 'price': 91.1, 'shares': 50}, {'name': 'CAT', 'price': 83.44, 'shares': 150}, {'name': 'MSFT', 'price': 51.23, 'shares': 200}, {'name': 'GE', 'price': 40.37, 'shares': 95}, {'name': 'MSFT', 'price': 65.1, 'shares': 50}, {'name': 'IBM', 'price': 70.44, 'shares': 100}]>>>
練習 2.6:把字典當作容器在使用索引而不是數字查詢某元素的地方,字典是一種用來跟蹤元素的很有用的方式。在 Python shell 中,嘗試使用字典:
>>> prices = { }>>> prices['IBM'] = 92.45>>> prices['MSFT'] = 45.12>>> prices... look at the result ...>>> prices['IBM']92.45>>> prices['AAPL']... look at the result ...>>> 'AAPL' in pricesFalse>>>
該 Data/prices.csv 檔案包含一系列帶有股票價格的行,看起來像下面這樣:
"AA",9.22"AXP",24.85"BA",44.85"BAC",11.27"C",3.72...
編寫 read_prices(filename)函式將諸如此類的價格集合讀取到字典中,字典的鍵代表股票的名字,字典的值代表股票的價格。
為此,從空字典開始,並且像上面做的那樣開始插入值。但是,現在正在從從檔案中讀取值。
我們將使用該資料結構快速查詢給定名稱的股票的價格。
這部分需要一些小技巧。首先,確保像之前做的那樣使用 csv 模組——無需在這裡重複發明輪子。
>>> import csv>>> f = open('Data/prices.csv', 'r')>>> rows = csv.reader(f)>>> for row in rows: print(row)['AA', '9.22']['AXP', '24.85']...[]>>>
另外一個小麻煩是 Data/prices.csv 檔案可能有一些空行在裡面。注意上面資料的最後一行是一個空列表——意味著那一行沒有資料。
這有可能導致你的程式因為異常而終止。酌情使用 try 和 except 語句捕獲這些異常。思考:使用 if 語句來防範錯誤的資料是否會更好?
編寫完 read_prices() 函式,請互動式地測試它並確保其正常工作:
>>> prices = read_prices('Data/prices.csv')>>> prices['IBM']106.28>>> prices['MSFT']20.89>>>
練習 2.7:看看你是否可以退休透過新增一些計算盈虧的語句到 report.py 程式,將所有的工作聯絡到一起。這些語句應該採用在練習 2.5 中儲存股票名稱的列表,以及在練習 2.6 中儲存股票價格的字典,並計算投資組合的當前值以及盈虧。