Python.use(better) #range: step08 -- def myrange_keys(f):
‖記事一覧‖ Python.use(better)《Python3.1》《復刻版》
range: 08 -- def myrange_keys(f):
組み込み関数と同等の機能を実現する過程を通して「関数」の理解を深めます。
※ Python1.5 で作成した例題を、Python3.1 で再構成しました。
事例:コードの解説
《 前ステップ 》で規定した関数が、任意の引数に対処できるようにします。
def myrange_args(f): def g(*args, **keys): if len(args)==2: (c,n),m = args,1 elif len(args)==1: c,n,m = 0,args[0],1 elif len(args)==0: c,n,m = 0,0,1 else: c,n,m = args return f(c, n, m, **keys) return g def myrange_keys(f): def g(c, n, m, **keys): for k,v in keys.items(): if k=="start": c=v if k=="stop" : n=v if k=="step" : m=v return f(start=c, stop=n, step=m) return g @myrange_args @myrange_keys def myrange(start=0, stop=None, step=1): c,n,m = start,stop,step s = [] while c < n: s.append(c) c += m return s
■ #1: 位置引数
def myrange_args(f): def g(*args, **keys): ... return f(c, n, m, **keys) return g
可変長の引数 args をもとに位置引数を確定して、関数呼び出し f() の実引数 c,n,m に指定します。
- 可変長の引数に対処するためのコードの断片を保持する、関数オブジェクト g をリターン値にします。
■ #2: キーワード引数
def myrange_keys(f): def g(c, n, m, **keys): ... return f(start=c, stop=n, step=m) return g
可変長の引数 keys をもとにキーワード引数を確定して、関数呼び出し f() の実引数 c,n,m に指定します。
- 可変長の引数に対処するためのコードの断片を保持する、関数オブジェクト g をリターン値にします。
■ #3: デコレーター
@myrange_args @myrange_keys def myrange(start=0, stop=None, step=1): ...
@ に続く関数定義の記述は、以下と同等です。
myrange = myrange_args(myrange_keys(myrange))
まず、myrange_args, myrange_keys を適用した後で、myrange を実行します。関数 myrange の本体にある、既存のコードの断片は不変です。 違いは、その前に @myrange_args, @myrange_keys を追記するだけです。
- myrange_args, myrange_keys では、myrange に影響を与えず、その機能を自由に拡張できます。
- myrange は、myrange_args, myrange_keys から影響を受けないので、そのコードは不変です。
《Tips》開放閉鎖原則:@ で始まるデコレーター構文を利用すると、開放閉鎖原則に沿って、簡潔で見通しの良いコードを記述できるだけでなく、要求仕様の変更にも柔軟に対処できます。
《Note》デコレーター構文の導入:この課題を作成したとき(Python1.5)はまだ、デコレーター構文はありません(Python2.4 以降)が、その本質は変わりません。
※ Release 2.4.4, documentation updated on 18 October 2006.
《発展課題》第3引数に負数を指定しても、
>>> s = range(10,0,-2); list(s) [10, 8, 6, 4, 2] >>> s = myrange(start=10,stop=0,step=-2); list(s) []組み込み関数 range と同じ結果が得られるように、myrange を改訂してください。
事例:モジュールを起動する
■ 全項目を確認する
全ステップの「項目」を確認するには、関数 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(8) >>> # -------------------------------------------------- step08 >>> for e in range(6): s = range(e+-1,4,2) print(s,list(s)) range(-1, 4, 2) [-1, 1, 3] range(0, 4, 2) [0, 2] range(1, 4, 2) [1, 3] range(2, 4, 2) [2] range(3, 4, 2) [3] range(4, 4, 2) >>> for e in range(6): s = myrange(e+-1,4,2) print("myrange(%d,4,2)"%(e+-1), s) myrange(-1,4,2) [-1, 1, 3] myrange(0,4,2) [0, 2] myrange(1,4,2) [1, 3] myrange(2,4,2) [2] myrange(3,4,2) [3] myrange(4,4,2) >>> for e in range(6): s = range(e+-1,4) print(s,list(s)) range(-1, 4) [-1, 0, 1, 2, 3] range(0, 4) [0, 1, 2, 3] range(1, 4) [1, 2, 3] range(2, 4) [2, 3] range(3, 4) [3] range(4, 4) >>> for e in range(6): s = myrange(e+-1,4) print("myrange(%d,4)"%(e-1), s) myrange(-1,4) [-1, 0, 1, 2, 3] myrange(0,4) [0, 1, 2, 3] myrange(1,4) [1, 2, 3] myrange(2,4) [2, 3] myrange(3,4) [3] myrange(4,4) >>> for e in range(5): s = range(e) print(s,list(s)) range(0, 0) range(0, 1) [0] range(0, 2) [0, 1] range(0, 3) [0, 1, 2] range(0, 4) [0, 1, 2, 3] >>> for e in range(5): s = myrange(e) print("myrange(%d)"%e, s) myrange(0) myrange(1) [0] myrange(2) [0, 1] myrange(3) [0, 1, 2] myrange(4) [0, 1, 2, 3] >>> for e in range(6): s = myrange(start=e+-1,stop=4,step=2) print("myrange(start=%d,stop=4,step=2)"%(e+-1), s) myrange(start=-1,stop=4,step=2) [-1, 1, 3] myrange(start=0,stop=4,step=2) [0, 2] myrange(start=1,stop=4,step=2) [1, 3] myrange(start=2,stop=4,step=2) [2] myrange(start=3,stop=4,step=2) [3] myrange(start=4,stop=4,step=2) [] >>> s = myrange(6,step=2); s [0, 2, 4] >>> s = myrange(6,start=3); s [3, 4, 5] >>> s = myrange(6,start=-1,step=2); s [-1, 1, 3, 5] >>> s = myrange(start=-1,step=2,stop=6); s [-1, 1, 3, 5] >>> s = myrange(start=-1,stop=3); s [-1, 0, 1, 2] >>> s = myrange(step=2,stop=6); s [0, 2, 4] >>>
新たに作成した関数 myrange が、組み込み関数 range に準拠していることを確認します。
- 引数で指定した範囲の数列を列挙した、リストが得られます。
さらに、組み込み関数 range と違って、関数 myrange では、キーワード引数を利用できます。
《余録》テストケース
def ex_range4(local, start, end, step): ex_range3(local, start, end, step) n = end - start + 1 X = '''for e in range({0}): s = myrange(start=e+{1},stop={2},step={3}) print("myrange(start=%d,stop={2},step={3})"%(e+{1}), s)'''.format(n, start, end, step) print_(X, local, "exec") source = ''' s = myrange(6,step=2); s s = myrange(6,start=3); s s = myrange(6,start=-1,step=2); s s = myrange(start=-1,step=2,stop=6); s s = myrange(start=-1,stop=3); s s = myrange(step=2,stop=6); s '''.split("\n") for e in source[1:-1]: print_(e, local)
↑TOP
》作業中です《
関連記事
|
8.1.5 でも「デコレーター」を話題にしているので、比較するのも一興です。 |
|
Chapter 2, Syntax Best Practices—Below the Class Level, presents iterators, generators, descriptors and so on, in an advanced way. でも「デコレーター」を話題にしているので、こちらも参考にすると良いでしょう。 |
□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□ | □ |