arguments.callee

なんかarguments.calleeとかいうものが流行っているらしい。

なので作ってみました。

@enable_arguments_callee
def foo():
    print arguments.callee

foo() #=> <function foo at 0x014916F0>

@enable_arguments_callee
def facto(n):
    if n == 1:
        return 1
    return n * arguments.callee(n - 1)

print facto(5) #=> 120
print repr(arguments.callee) #=> None

こんな感じの挙動をすればいいんでしょうか。

実装は下のような感じ。デコレータを使って、関数呼び出しの前後に「その関数をargumentsオブジェクトの持っているスタックにプッシュする」「ポップする」という処理を付け加えています。デコレータというのは引数に関数を取って関数を返す関数です。あとargumentsオブジェクトはビルトイン名前空間に入れてどこからでも「arguments」という名前で呼び出せるようにしました。

import __builtin__
class Arguments(object):
    value = [None]
    def push(self, f):
        self.value.append(f)
        self.callee = f

    def pop(self):
        self.value.pop()
        self.callee = self.value[-1]

__builtin__.arguments = Arguments()
del Arguments

def enable_arguments_callee(f):
    def wrap(*args, **kw):
        arguments.push(f)
        result = f(*args, **kw)
        arguments.pop()
        return result
    return wrap

問い:この実装の問題点は何でしょう。

とりあえずarguments.calleeに関する処理はあくまでenable_arguments_calleeデコレータの中で行っているので、たとえばenableした関数Aからしていない関数Bを呼んでその中でarguments.calleeを参照した場合その値は関数Aです。あと関数にはいるのは関数の呼び出し時のみだという仮定を置いているのでスレッドを使ったりすると壊れます。