ASPN : Python Cookbook : Autoslots

インスタンス変数を固定する__slots__というのがある。それを自動で設定するメタクラスのレシピ。
inspectモジュールでソースコードを取ってきて構文解析させるという強引な方法をとっているようだ。

メタクラスについてはASPN : Python Cookbook : Semi-automatic resource management with AutoCloseで少し勉強した。

調べたこと。

  • inspectモジュール
  • compile関数
  • _astモジュール
  • __slots__変数

inspectモジュール

オブジェクトから、ソースコードとかソースファイルとかを調べるのに使える。
このレシピではgetsource(object)でソースコードを取ってきている。

compile関数

組み込み関数。文字列をコンパイルする!
compile(string, filename, kind[, flags[, dont_inherit]])
戻り値はコードオブジェクト(_ast.Module)。
コードオブジェクトの実行はeval()で。

_astモジュール

Python2.5で導入された。Abstract Syntax Treesの略らしい。
Pythonソースコード構文解析した結果のツリーを扱うことができる。

Module->ClassDef->FunctionDef->...
で文まで分解できる。
その下は、代入文はAssignオブジェクトだったりprint文はPrintオブジェクトだったり。
レシピでは、Assignの場合にtarget属性を見てインスタンス変数名を取得している。

__slots__変数

クラス変数。
__slots__ = ["var1", "var2"]
のように変数名をセットすると、インスタンス変数を制限できる。objectを継承した新スタイルクラスで有効。
未定義のインスタンス変数を禁止する使い方ができるほかに、__dict__を作らないのでメモリなどのコストが少なくて済むというメリットがある。

class SlotsTest(object):
    __slots__ = ["var1", "var2"]

    def __init__(self):
        pass

としておくと以下のようになる。

>>> t = SlotsTest()
>>> dir(t)
['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', 
'__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', 
'__slots__', '__str__', 'var1', 'var2']

dictがないのが確認できる。

>>> t.var1 = "a"
>>> t.var2 = "b"
>>> t.var3 = "c"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'SlotTest' object has no attribute 'var3'

__slots__に定義していない属性は使えなくなっている。

その他

このテクニックを何に使えるか。
_astモジュールでの解析は、命名規約とかのルールに違反してないかのチェックに使えるかな。他にもやりようはありそうだけど、Python自体に構文解析させるのが一番確実だろう。
ソースコード解析して、ドキュメント作成の補助にもなるかも。
__slots__はパフォーマンスが気になる場面(オブジェクトを大量に作るとか)では積極的に使うと良さそう。