名前が存在する場所
名前のスコープとは、その名前を使えるプログラム上の範囲です。関数の中で作った名前はローカルで、その関数が動いている間だけ存在し、外からは見えません。ファイルの最上位で作った名前はグローバルで、関数から読めます。Pythonは名前に出会うと、まず関数内、次に外側の関数、次にファイル、最後に組み込みの名前の順に探します。
rate = 0.001 # global: defined outside any function
def fee(notional):
minimum = 1.0 # local: exists only during the call
return max(minimum, notional * rate)
print(fee(36850.0)) # 36.85
# print(minimum) # would raise NameError: minimum is not defined代入するとローカル名ができる
関数の中からグローバルな名前を読むのは問題ありません。しかし関数の中で名前に代入すると、同名のグローバルがあっても新しいローカル名ができ、グローバルはそのまま残ります。これでデータが守られますが、関数からグローバルの状態を変えるのに余分なキーワード(global)が必要になる理由でもあり、これはめったに使わないはずのものです。値は引数で渡し、結果は return で返しましょう。
# Assigning inside a function makes a new local name
balance = 1000
def deposit(amount):
balance = amount # a different, local balance
return balance
print(deposit(50)) # 50
print(balance) # 1000 (the global one is unchanged)関数を作る関数
関数の中に別の関数を定義して return で返せます。内側の関数は、外側の関数が終わったあとでも、外側の変数にアクセスし続けます。この「関数と、それが覚えている値」のひとまとまりがクロージャです。設定から使えるツールを作る便利な方法で、株式用と ETF 用の別々の計算機を作れます。
# A closure remembers the variables of the function that made it
def make_fee_calculator(rate, minimum):
def fee(notional):
return max(minimum, notional * rate)
return fee
stock_fee = make_fee_calculator(0.001, 1.0)
etf_fee = make_fee_calculator(0.0005, 0.5)
print(stock_fee(36850.0)) # 36.85
print(etf_fee(36850.0)) # 18.425呼び出しの間で続く状態:nonlocal
クロージャは、覚えている値を変更することもできます。代入するとローカル名が新しくできてしまうため、内側の関数は先に nonlocal count と宣言します。「この名前は外側の関数のもの」という意味です。外側の関数を呼ぶたびに独立した記憶が作られるので、2つのカウンターが干渉することはありません。注文IDの発行や移動平均が、グローバル変数なしで状態を保てるのはこの仕組みのおかげです。
# nonlocal lets the inner function rebind the outer variable
def make_counter():
count = 0
def next_id():
nonlocal count
count += 1
return count
return next_id
order_ids = make_counter()
print(order_ids(), order_ids(), order_ids()) # 1 2 3
other = make_counter()
print(other()) # 1 (its own count)# State between calls: an exponential moving average
def make_ema(alpha):
value = None
def update(price):
nonlocal value
value = price if value is None else alpha * price + (1 - alpha) * value
return value
return update
ema = make_ema(0.5)
print(ema(100.0), ema(110.0), ema(110.0)) # 100.0 105.0 107.5クロージャが保持するのは変数であり、コピーではない内側の関数は実行時に変数を参照するため、最新の値が見えます。カウンターや平均が動くのはそのおかげです。同じ理由で、ループの中で作ったクロージャは、引数やデフォルト値で渡さない限り、ループ変数の最終的な値を見ることになります。
- 関数内で代入した名前はローカル。データは引数で渡し、
returnで返す - 関数を作って返す関数は、設定をツールにまとめる
- 呼び出しの間で保持したい状態には
nonlocal(クロージャごとに別の記憶)
次はリスト内包表記です。別のリストから新しいリストを、読みやすい1行で作ります。
ラボ:採点される内容
make_counterで count を覚えるカウンターを作る25点make_fee_calculatorで手数料の設定を関数にまとめる25点make_emaで呼び出しをまたいで移動平均を保つ25点make_limit_checkerで上限から価格チェックを作る25点