Python.use(better, src=”PyPy”) #004: クラス pycodegen.AbstractCompileMode
|中級篇|
Python.use(better, src=”PyPy”) # ソースコードの歩き方《復刻版》
ソースコードの歩き方《PyPy1.2》
PyPy1.2 のリリースを機に、PyPy1.1 版を再構成したものです。
※ compiler の傘下にあるモジュールは、PyPy1.1.0 からの変更はありません。
クラス pycodegen.AbstractCompileMode
出生の秘密を探るために、生まれたときの状況(インスタンス生成)を確認しておきます。
def compile(source, filename, mode, flags=None, dont_inherit=None): ... gen = Expression(source, filename)
クラス呼び出し Expression() の引数 source,filename は、関数呼び出し compile() のときに譲渡されたものです。生成した Expression インスタンスを初期設定する術は、親クラス AbstractCompileMode 譲りなので、特殊メソッド __init__ を頼りに探ります。そこでは、
$ cat compiler/pycodegen.py ... class Expression(AbstractCompileMode): ... class AbstractCompileMode: ... def __init__(self, source, filename): self.source = source self.filename = filename self.code = None
譲渡された引数 source,filename は、そのままインスタンス属性として保持されます。そして、目的とする属性 .code には(当然のことながら)なにもない None のが分かります。
《TIPS》過程を探るテストケース:プログラミング(作譜)では、その途上にある現在地を確認する術が必要です。そこに至るルート(過程)が正しいことを確認するには(たとえば、属性 .code などに)適当な値を設定して、その動作を検証しておきます。この先で道に迷っても、元の場所に戻る術があれば安心です。□
■ 生まれいずる悩み(インスタンス属性とクラス属性)
出生の秘密(インスタンスの初期設定)が明らかになったので、現実(メソッド _get_tree)に戻ります。すると、
$ cat compiler/pycodegen.py
...
class AbstractCompileMode:
...
def _get_tree(self):
tree = parse(self.source, self.mode)
misc.set_filename(self.filename, tree)
syntax.check(tree)
return tree
次に進む扉を開く鍵 tree を得るには、インスタンスに固有の属性(self.source, self.filename)とは別に、第3のアイテム self.mode が不可欠です。ところが、身に覚えがありません。インスタンス属性に self.mode は存在しないのです。
ここで、self をインスタンス自身だと思い込むと、迷子になります。なぜなら、self.mode はインスタンスに固有の属性ではなく、他のインスタンスと共有(情報交換)するためのクラス属性だからです。そこで、冷静に辺を見回すと、
$ cat compiler/pycodegen.py ... class AbstractCompileMode: ... mode = None # defined by subclass
が見つかり、迷子にならずにすみます。ところが、そこには = None とあるだけです。まるで、手掛かりを追っていくと、関係者が謎の死を遂げるかのような展開に、再び奈落の底へ突き落とされます。