7月 14 2009

俺も Amazon の Proxy を Google App Engine で作ってみた

Published by HoLY under tech

んで GitHub で公開。

http://github.com/holysugar/gae-amazon-proxy/tree/master

しかし作ってみた、のはいいがもういろいろ出てまわってるんだよなあ。:)

基本的にはただただしさんの amazon-auth-proxy の Python での GAE への移植。

たぶん、他の人のと似たようなバグがあるだろう。チェックしておかねばなあ。

最終目的は isbn anchor plugin 用の(透過でない) Proxy なんで、この Proxy を(おおっぴらに)公開はいまのところしてない。

今の仕事は Python を使ってがりがり書いてるせいか、特に Python で書くことに何の違和感もなくなってしまったなあ。何の制約もなければ Ruby やら Rails やらを使ってるんだけれども、 GAE 上で何かするなら Python でいいべ、と。これくらいのコードならすごく書きやすいし。

う、テスト書いてませんごめんなさい……(誰に謝ってるんだ)。

Tags: ,

No responses yet

5月 07 2009

Python のデフォルト引数は一度しか評価されない

Published by HoLY under tech

最近は仕事なんかで Python でコードを書くことが結構あるのだけど、知らずに惑ったことの一つに、関数のデフォルト引数が定義時にしか評価されない仕様というのがある。

デフォルトパラメタ値は関数定義を実行する際に値評価されます。 これは、デフォルトパラメタの式は関数を定義するときにただ一度だけ評価され、同じ”計算済みの”値が全ての呼び出しで使われることを意味します。

例えば無理矢理な例だけど、こんな風に使うことも可能。関数ローカルの変数扱いですな。

def fib(num, cache=[0,1]):
    assert len(cache) > 1

    if len(cache) > num:
        return cache[num]
    else:
        cache[num:num] = [fib(num-1) + fib(num-2)]
        return cache[num]

print fib(10) # => 55

試しに、この仕様に反して、 Python でも呼びだしの度にデフォルト引数が初期化されるようなコードを、デコレータを使って書いてみた。 デコレータというのは簡単に言うと関数に被せるラッパーを書くためのシンタックスシュガー。

def newdefaults(fn):
    import copy
    arity = fn.func_code.co_argcount
    newdefaults.defaults = copy.deepcopy(fn.func_defaults)

    def newfn(*args):
        args = list(args)

        while len(args) < arity:
            index = len(args) - arity + len(newdefaults.defaults)
            if index < 0: # 引数たりない
                return fn(*args)
            arg = newdefaults.defaults[index]
            args.append(copy.deepcopy(arg))
        return fn(*args)

    return newfn

if __name__ == '__main__':

    @newdefaults
    def f(v1, v2, d=0, a=[0]):
        print "f(%s, %s, %s, %s)" % (v1, v2, d, a)
        a.append(v1)
        return a

    assert f(1,0) == [0,1]
    assert f(2,0) == [0,2]
    try:
        f()
        assert False
    except TypeError, e:
        print e # OK
    except:
        print "Unexpected error"
        assert False

結果はこう。

f(1, 0, 0, [0])
f(2, 0, 0, [0])
f() takes at least 2 arguments (0 given)
  • *args および **kwargs を引数にとる関数には適用できないはず。
  • ポイント: 関数の様子はある程度は function のプロパティ、f.func_defaultsf.func_code などから分かる。もっと詳しく調べたいときは import inspect
  • deepcopy しちゃうとまずい時もありそうだけどまあいいか。

Tags:

No responses yet