Pythonでmixin

昨日のまとめ。


まずモジュールを利用する場合はモジュールに関して以下を知っておく必要がある。


sys.modules[module_name] = module_objで、モジュールを追加することができる。ここで、module_objには「属性を持ったオブジェクト」なら何でもOK。つまり、以下の3通りのどれでもモジュールとして認識できる。int値など全てのオブジェクトもモジュールになり得るが、Noneはimportした際にエラーがでる。また、reload()の引数はモジュールオブジェクト以外だとエラーになる。ここでは属性を持てるもののみ考える。

  • 関数
  • クラス
  • モジュール


ここで、属性が関数である場合にクラスだけ特殊な意味を持つ。以下を考えてみる。

>>> class Foo:
...   def foo(self):pass
...
>>> class Bar:
...   def foo(self):pass
...
>>> Bar.foo = Foo.foo
>>> Bar.foo
<unbound method Foo.foo>
>>> Bar.foo(Foo())
>>> Bar.foo(Bar())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Foo instance as first argument (got Bar instance instead)

つまり、属性が関数の場合、(恐らく参照時に)関数が所属するクラスの情報を持ったメソッドに自動的に昇格する。


ここでさらに、Pythonで何らかの機能を追加する方法は以下の通り。

  • 継承
  • 委譲
  • デコレータ
  • import
  • 属性をコピー


ここで、mixinとは何かについて考える。私の考えでは、以下の2つで表現できるのではと考える。実はmixinとして成立する条件は私自身理解していない。wikipediaでmixin調べたけどよく分からなかった。

  • 属性をコピーする
  • コピーされた属性がコピー元の情報を持たない


すると、継承、委譲は使えない。デコレータももちろん使えない。結局、setattr()やa=bやFoo.__dict__(新スタイルクラスの場合はdictproxyでread-only)もしくは、importのどれかしかないのかなと思う。『Pythonクックブック 第2版』とかで__getattr__などを利用しDelegateする方法が載っているが、それだとmixinではないのかなと思っている。


結局、クラスの定義部分で一気にmixinするにはimportする方法が1つ。後は、inspectを利用して何かできないだろうか?あと、メタクラスを利用したら継承になるのでダメなのだろうか?やはりまずmixinの本質を理解しないとダメかも。