名前で値を引く
辞書(dict)はキーと値を対応づけます:{"AAPL": 184.5, "MSFT": 410.2}。リストが位置で要素を見つけるのに対し、辞書はキーで見つけ、エントリ数が増えても検索は速いままです。prices["AAPL"] で値を読み、prices["TSLA"] = 251.0 で新しいエントリを追加するか既存のものを置き換え、"AAPL" in prices でキーがあるかを調べます。存在しないキーを角かっこで読むと KeyError になります。
prices = {"AAPL": 184.5, "MSFT": 410.2}
# Read, add and replace
print(prices["AAPL"]) # 184.5
prices["TSLA"] = 251.0
prices["AAPL"] = 185.0
print(prices) # {'AAPL': 185.0, 'MSFT': 410.2, 'TSLA': 251.0}
# in tests the keys; a missing key raises KeyError
print("MSFT" in prices, "GOOG" in prices) # True False
try:
prices["GOOG"]
except KeyError as error:
print("no such key:", error)get:キーがなくてもエラーではない
prices.get("GOOG", 0.0) は値を返し、キーがなければ指定した既定値(指定しなければ None)を返し、エラーにはなりません。これでカウンターが簡単に作れます。counts[symbol] = counts.get(symbol, 0) + 1 は新しいシンボルなら 0 から始め、出てくるたびに1を足します。キーがないことがバグで、それに気づきたいときは []、ないのが普通なら get を使います。
# get() with a default never raises
prices = {"AAPL": 184.5}
print(prices.get("AAPL", 0.0)) # 184.5
print(prices.get("GOOG", 0.0)) # 0.0
print(prices.get("GOOG")) # None
# Counting with get
counts = {}
for symbol in ["AAPL", "MSFT", "AAPL", "AAPL"]:
counts[symbol] = counts.get(symbol, 0) + 1
print(counts) # {'AAPL': 3, 'MSFT': 1}辞書をたどる
単純な for key in prices はキーを順にたどります。値は prices.values()、(キー, 値) のペアは prices.items() で得られ、ループ内でアンパックします:for symbol, price in prices.items()。辞書はキーが追加された順序を覚えています。同じ辞書をループしている間にキーを追加・削除してはいけません。コピーをたどるか、新しい辞書を作りましょう。
# keys(), values() and items()
positions = {"AAPL": 200, "MSFT": 50, "GOOG": 0}
prices = {"AAPL": 184.5, "MSFT": 410.2}
for symbol in positions:
print(symbol, end=" ") # AAPL MSFT GOOG
print()
total = 0.0
for symbol, quantity in positions.items():
total += quantity * prices.get(symbol, 0.0)
print(round(total, 2)) # 57410.0削除・結合・コピー
prices.pop("AAPL", None) はキーを削除してその値を返し(なければ既定値)、del prices["AAPL"] は削除し、なければ KeyError になります。a.update(b) は b のエントリを a にコピーして a を変更し、{**a, **b} や a | b は共通のキーで b が優先される新しい辞書を作ります。リストと同じく copy = prices は名前が増えるだけなので、本当のコピーには dict(prices) か prices.copy() を使います。
# Removing and merging
book = {"AAPL": 200, "MSFT": 50}
sold = book.pop("MSFT", 0)
print(sold, book) # 50 {'AAPL': 200}
# {**a, **b} builds a new dict; b wins on shared keys
defaults = {"fee": 0.001, "currency": "USD"}
custom = {"fee": 0.0005}
settings = {**defaults, **custom}
print(settings) # {'fee': 0.0005, 'currency': 'USD'}
print(defaults) # unchanged: {'fee': 0.001, 'currency': 'USD'}キーにできるもの
キーはハッシュ可能でなければならず、実際には変更できないものという意味です。文字列、数値、それらのタプルは使えますが、リストや辞書は使えません。タプルは (symbol, venue) のような複合キーに向いています。値には別のリストや辞書を含め何でも入れられます。同じキーは1つしか存在できないので、もう一度代入すると古い値が置き換わります。
# A tuple as a compound key, a list as a value
best = {("AAPL", "NYSE"): 184.6, ("AAPL", "ARCA"): 184.5}
print(best[("AAPL", "ARCA")]) # 184.5
fills = {"AAPL": [100, 250], "MSFT": [75]}
fills["AAPL"].append(40)
print(fills) # {'AAPL': [100, 250, 40], 'MSFT': [75]}
# A list cannot be a key
try:
{["AAPL"]: 1}
except TypeError as error:
print("not hashable:", error)prices[symbol] は「このキーは必ずある」という意味で、なければ大きく失敗します。prices.get(symbol, default) は「なくてもよい」という意味です。本物のバグを既定値で隠すのも、普通のケースで落ちるのと同じくらい悪いので、どちらの意図かを決めましょう。ラボでは、渡した辞書が変更されていないことも確認します。d[key]、d[key] = value、key in d:読む・書く・キーを調べるd.get(key, default):安全な検索。d.items():ループ用のペアd.pop(key, default)、del d[key]、d.update(other)、{**a, **b}:削除と結合
次は集合です。重複のない値のコレクションで、所属判定が速く、集合演算も簡潔に書けます。
ラボ:採点される内容
latest_pricesでシンボルごとの最新価格を保持する25点count_symbolsで各シンボルの出現回数を数える25点portfolio_valueで2つの辞書からポートフォリオの価値を求める25点merge_positionsで2つのポジション帳を統合する25点