Python.use(better) #デコレーター:開放閉鎖原則

記事一覧 Python.use(better)《Python3.1》《復刻版》

range: 05 -- @ def myrange(c, n):

《著》森こねこ、小粒ちゃん+∞《監修》小泉ひよ子とタマゴ倶楽部
第0版♪2001/03/02 ● 第1版♪2003/05/25 ● 第2版♪2004/06/01 ● 第3版♪2009/02/28

組み込み関数と同等の機能を実現する過程を通して「関数」の理解を深めます。
※ Python1.5 で作成した例題を、Python3.1 で再構成しました。

事例:モジュールを起動する

■ 全項目を確認する

全ステップの「項目」を確認するには、関数 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 -- @ def myrange(c, n):
 6: step06 -- def myrange(c, n, m):
 7: step07 -- def myrange(start=0, stop=None, step=1):
 8: step08 -- def g(*args,**keys)
>>>
■ 各項目を実行する

各ステップの「動作」を確認するには、関数 do に実引数を指定します。

>>> do(5)
>>> # -------------------------------------------------- step05
>>> for e in range(5):
    s = range(e+-1,3)
    print(s,list(s))
range(-1, 3) [-1, 0, 1, 2]
range(0, 3) [0, 1, 2]
range(1, 3) [1, 2]
range(2, 3) [2]
range(3, 3) 
>>> for e in range(5):
    print("myrange(%d,3)"%(e-1), myrange(e+-1,3))
myrange(-1,3) [-1, 0, 1, 2]
myrange(0,3) [0, 1, 2]
myrange(1,3) [1, 2]
myrange(2,3) [2]
myrange(3,3) 
>>> for e in range(4):
    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]
>>> for e in range(4):
    print("myrange(%d)"%e, myrange(e))
myrange(0) 
myrange(1) [0]
myrange(2) [0, 1]
myrange(3) [0, 1, 2]
>>>

新たに作成した関数 myrange が、組み込み関数 range に準拠していることを確認します。

  • 引数で指定した範囲の数列を列挙した、リストが得られます。

事例:コードの解説

前ステップ 》で規定した関数が、任意の引数に対処できるようにします。

    def myrange_args(f):
        def g(*args):
            if len(args)==1:
                c,n = 0,args[0]
            else:
                c,n = args
            return f(c,n)
        return g

    @myrange_args
    def myrange(c, n):
        s = []
        while c < n:
            s.append(c)
            c += 1
        return s
■ #1: 可変長の引数
    def myrange_args(f):
        def g(*args):
            ...
            return f(c,n)
        return g

可変長の引数に対処するためのコードの断片を保持する、関数オブジェクト g をリターン値にします。

  • 関数呼び出し f() には、引数リスト args に対応する、実引数 c,n 指定します。
■ #2: デコレーター
    @myrange_args
    def myrange(c, n):
        ...

@ に続く関数定義の記述は、以下と同等です。

    myrange = myrange_args(myrange)

まず、myrange_args を適用した後で、myrange を実行します。関数 myrange の本体にある、既存のコードの断片は不変です。 違いは、その前に @myrange_args を追記するだけです。

  • myrange_args では、myrange に影響を与えず、その機能を自由に拡張できます。
  • myrange は、myrange_args から影響を受けないので、そのコードは不変です。

《Tips》仕様変更の影響:@ で始まるデコレーター構文を利用すると、開放閉鎖原則に沿って、簡潔で見通しの良いコードを記述できるだけでなく、要求仕様の変更にも柔軟に対処できます。

《余録》テストケース

def step05():
    ...

    ## ----------------------------------------
    local = locals()
    ex_range2a(local, -1, 3)

def ex_range2(local, start, end):
    n = end - start + 1
    X = '''for e in range({0}):
    s = range(e+{1},{2})
    print(s,list(s))'''.format(n, start, end)
    print_(X, local, "exec")

    X = '''for e in range({0}):
    print("myrange(%d,{2})"%(e-1), myrange(e+{1},{2}))'''.format(n, start, end)
    print_(X, local, "exec")

def ex_range2a(local, start, end):
    ex_range2(local, start, end)    #

    X = '''for e in range({0}):
    s = range(e)
    print(s,list(s))'''.format(end - start)
    print_(X, local, "exec")

    X = '''for e in range({0}):
    print("myrange(%d)"%e, myrange(e))'''.format(end - start)
    print_(X, local, "exec")

》こちらに移動中です《
TOP


関連記事

Last updated♪2009/11/06