2008-02-26

Python のネストした関数での変数のスコープ

Python ではネストした関数を記述でき,その関数から外側のブロックの変数が見える.

しかし読むことはできても書くことはできない(!!).しかも,書くとエラーになるのではなく,内側のブロックにローカル変数を作成してしまい,その関数を呼ぶと「初期化する前に参照」などのわかりにくいエラーが発生する.

def f():
    def g():
        if first:
            print "first"
            first = False
        else:
            print "not first"
    first = True
    g()

は 3 行目で実行時エラー.

謎な点:

  • g()first = False を消すとエラーが出なくなる.ということは文 first = False をバイトコンパイル時に読んでレキシカルにローカル変数を作成しているわけだが,このようなことはドキュメントのどこに記述してあるのか?

Effective Python/スコープと名前空間 によると Python 3 では nonlocal なるステートメントが用意されて,外側のローカル変数も書けるようになるらしい.

2008-02-20

継承についての小考察

継承でスペースを犠牲にして利便性を向上するケースに出会った.

typedef void (*TidyItemProc)(void *item);

struct List {
    /* ... */
    void MakeEmpty(TidyItemProc proc);
};

struct XList : public List {
    void MakeEmpty() { List::MakeEmpty(this->tidy_item_proc); }
    TidyItemProc tidy_item_proc;
};

でも実はこれは継承を用いるべきではない.XList is-a List になっていない.これは本当は集約を用いるべきケース.

struct XList {
    List list;
    void MakeEmpty() { list.MakeEmpty(this->tidy_item_proc); }
    TidyItemProc tidy_item_proc;
};

しかし集約でメソッドをリダイレクトするのは面倒だ.