Python.use(better) #range:《余録》@spyOn
‖記事一覧‖ Python.use(better)《Python3.1》
range:《余録》@spyOn
《著》森こねこ、小粒ちゃん+∞《監修》小泉ひよ子とタマゴ倶楽部
第0版♪2001/03/02 ● 第1版♪2003/05/25 ● 第2版♪2004/06/01 ● 第3版♪2009/02/28
組み込み関数と同等の機能を実現する過程を通して「関数」の理解を深めます。
※ Python1.5 で作成した例題を、Python3.1 で再構成しました。
事例:コードの解説
from time import time def spyOn(f): def g(*args): t1 = time(); r = f(*args); t2 = time() return r, t2-t1 return g
def myrange_args(f): ... def myrange_keys(f): ... def myrange0(start=0, stop=None, step=1): while start < stop: start += step yield start @myrange_args @myrange_keys def myrange1(start=0, stop=None, step=1): s = [] while start < stop: s.append(start) start += step return s @myrange_args @myrange_keys def myrange2(start=0, stop=None, step=1): while start < stop: start += step yield start
■ #1: yield 文を利用する:デコレーターを伴わない
def myrange0(start=0, stop=None, step=1): while start < stop: ... yield start
位置引数だけに頼って、キーワード引数を利用しないと、省略可能な引数が限定されます。
■ #2: リストを利用する:デコレーターを伴う
@myrange_args @myrange_keys def myrange1(start=0, stop=None, step=1): s = [] while start < stop: ... return s
数列を列挙したリストを生成して、関数のリターン値にします。
■ #3: yield 文を利用する:デコレーターを伴う
@myrange_args @myrange_keys def myrange2(start=0, stop=None, step=1): while start < stop: ... yield start
yield 文を利用すると、ジェネレーター関数になります。
《Note》トリレンマ:「プログラム vs. プログラミング」どちらの効率を優先するか、そのトレードオフ(デコレーターの導入/ジェネレーターの導入)だけではなく、利用者の便宜(キーワード引数の導入)にも配慮する必要があります。
事例:モジュールを起動する
■ 全項目を確認する
全ステップの「項目」を確認するには、関数 do を利用します。
$ python -i my_range.py >>> do() 0: step00 -- def myrange(n): 1: step01 -- while n: 2: step02 -- s[0:0] = str(n) 3: step03 -- def myrange(c, n): 4: step04x -- def myrange(*args): 5: step05 -- @myrange_args 6: step06 -- def myrange(c, n, m): 7: step07x -- def myrange_args(f): 8: step08 -- def myrange_keys(f): 9: step09 -- def g(*args, **keys): 10: step10 -- yield start 11: step_spyOn -- @spyOn >>>
■ 各項目を実行する
各ステップの「動作」を確認するには、関数 do に実引数を指定します。
>>> do(11) >>> # -------------------------------------------------- step_spyOn >>> range(0,1000000,1) range (None, 0.07149791717529297) myrange0 (None, 0.3966100215911865) myrange1 (None, 0.5240530967712402) myrange2 (None, 0.3833169937133789) >>> range(0,10000000,10) range (None, 0.0773470401763916) myrange0 (None, 0.4027891159057617) myrange1 (None, 0.5115780830383301) myrange2 (None, 0.37252092361450195) >>> do(11) >>> # -------------------------------------------------- step_spyOn >>> range(0,1000000,1) range (None, 0.07951593399047852) myrange0 (None, 0.38605213165283203) myrange1 (None, 0.5196981430053711) myrange2 (None, 0.3934459686279297) >>> range(0,10000000,10) range (None, 0.0772550106048584) myrange0 (None, 0.4185311794281006) myrange1 (None, 0.5148370265960693) myrange2 (None, 0.38182497024536133) >>> do(11) >>> # -------------------------------------------------- step_spyOn >>> range(0,1000000,1) range (None, 0.07607197761535645) myrange0 (None, 0.37715697288513184) myrange1 (None, 0.547645092010498) myrange2 (None, 0.3761270046234131) >>> range(0,10000000,10) range (None, 0.07460308074951172) myrange0 (None, 0.4025721549987793) myrange1 (None, 0.5118789672851562) myrange2 (None, 0.3659961223602295) >>>
1,000,000個の数列を生成させるとともに、それを何度か実行した結果を比較します。
- yield 文を利用すると、リストと比べて、実行効率が改善されるものの、
- 組み込み関数 range と比べて、約5倍の処理時間を要するのが分かります。
また、
- デコレーターを導入しても、その影響はほとんどないことが分かります。
《余録》テストケース
local = locals() cases = "range","myrange0","myrange1","myrange2", s2 = "%%-%ds"%max(len(e) for e in cases) times = 10**6 s1 = "%s(0,%d,1)" print(">>>",s1%(cases[0],times)) for s in cases: @spyOn def ex(n): for e in eval(s1%(s,n),local): pass print(s2%s,ex(times)) times = 10**7 s1 = "%s(0,%d,10)" print(">>>",s1%(cases[0],times)) for s in cases: @spyOn def ex(n): for e in eval(s1%(s,n),local): pass print(s2%s,ex(times))
》こちらに移動中です《
↑TOP