Python:アクセサの生成

Pythonの練習がてら、アクセサの生成をやってみる。どうせ探したらいっぱいコードが転がってるだろうし、練習にはうってつけかな、と。

まず、ダメそうだけど、Rubyをやってる人からするとこうかきたい、というコード。

python code
  1. class Test(Accessor):
  2.   attr_accessor("__test", "__test2", "test3", "_test4")
  3.  
  4.   def __init__(self):
  5.     self.__test = "test_value"
  6.     self.__test2 = "test2_value"
  7.     self.test3 = "test3_value"
  8.     self._test4 = "test4_value"
  9.  

こんな感じ。まぁ、絶対にダメそうだ(笑
でも組み込みとはいえ、classmethodやstaticmethodみたいなのもあるから無理やりにならできるのかもしれない。

python code
  1. def test():
  2.   print sys._getframe(1).f_code.co_name
  3.  
  4. class Test:
  5.   test()
  6.  
  7. #=> 文字列"Test"を出力
  8.  

こんな感じで呼び出しもとのクラス名は取得できる。ならevalすれば、というところなのだが予想通り

python code
  1. def test():
  2.   exec "cls = %s" % sys._getframe(1).f_code.co_name
  3.  
  4. class Test:
  5.   test()
  6.  
  7. #=> NameError: name 'Test' is not defined
  8.  

この時点ではクラスオブジェクトの生成が完了していないので無理なようだ。Rubyのようにはいかない。

結局のところ[__metaclass__](http://www.python.jp/doc/2.4/ref/metaclasses.html "Python metaclass")を使うことになる。
マニュアルにもズバリ > メタクラスは限りない潜在的利用価値を持っています。これまで試されてきたアイデアには、ログ記録、インタフェースのチェック、自動デリゲーション、自動プロパティ生成、プロキシ、フレームワーク、そして自動リソースロック/同期といったものがあります。

と書いてあるのだ。

  • 後からもアクセサを追加したい(メソッドとしても独立させたい)
  • 当然、読み出しと書き込み、そして両方を行えるインターフェイス
  • オーバーライドはできないとまずい

というアクセサ生成としては至極当たり前なことを考えつつ、コードを書いてみる。

python code
  1. # vim: fileencoding=utf-8
  2. from itertools import *
  3.  
  4. def property_accessor(cls, *names):
  5.   map(lambda n : _add_setter(cls, n[0], n[1]) or
  6.                  _add_getter(cls, n[0], n[1]),izip(names, _real_names(cls, names)))
  7.  
  8. def property_reader(cls, *names):
  9.   map(lambda n : _add_getter(cls, n[0], n[1]),izip(names, _real_names(cls, names)))
  10.  
  11. def property_writer(cls, *names):
  12.   map(lambda n : _add_setter(cls, n[0], n[1]),izip(names, _real_names(cls, names)))
  13.  
  14. def _real_names(cls, names) :
  15.   cls_name = cls.__name__
  16.   return imap(lambda n : n.startswith("__") and "_%s%s"%(cls_name, n) or n, names)
  17.  
  18. def _add_setter(cls, name, real_name) :
  19.   setter_name = "set_%s" % name.lstrip("_")
  20.   if cls.__dict__.has_key(setter_name): return
  21.   setattr(cls, setter_name, lambda self, v: setattr(self, real_name, v))
  22.  
  23. def _add_getter(cls, name, real_name) :
  24.   getter_name = "get_%s" % name.lstrip("_")
  25.   if cls.__dict__.has_key(getter_name): return
  26.   setattr(cls, getter_name, lambda self: getattr(self, real_name))
  27.  
  28. class AccessorType(type):
  29.   def __new__(cls, class_name, class_bases, classdict):
  30.     cls = type.__new__(cls, class_name, class_bases, classdict)
  31.     list = ["__accessor__", "__reader__", "__writer__"]
  32.     methods = imap(lambda n: eval("property_%s"%n.strip("_")), list)
  33.     map(lambda n:
  34.           classdict.has_key(n[0]) and
  35.             n[1](cls, *classdict[n[0]]), izip(list, methods))
  36.     return cls
  37.  
  38. class Accessor:
  39.   __metaclass__ = AccessorType
  40.  
  41. class Test(Accessor):
  42.   __accessor__ = ["__test2", "test3", "_test4"]
  43.   __reader__ = ["__test"]
  44.  
  45.   def __init__(self):
  46.     self.__test = "test_value"
  47.     self.__test2 = "test2_value"
  48.     self.test3 = "test3_value"
  49.     self._test4 = "test4_value"
  50.  
  51.   def get_test3(self) :
  52.     return "changed_test3_value"
  53.  
  54. obj = Test()
  55.  
  56. print obj.get_test2()
  57. # => "test2_value"
  58. print obj.get_test3()
  59. # => "changed_test3_value"
  60.  
  61. property_writer(Test, "__test")
  62. obj.set_test("new_test_value")
  63. print obj.get_test()
  64. # => "new_test_value"
  65.  

テストもろくにしてないし、汚いコードだけどなんとなくそれっぽい動き。だいたい40行くらいで実装できる。パフォーマンスを考えなければitertoolsがなくても大丈夫。

そして、回答をさがしてみる。とりあえず「python accessor __metaclass__」あたりでググるとすぐにコードが見つかる。

*ASPN : Python Cookbook : Generating get/set methods using a metaclass

見事なくらい同じようなコード。ただ、自分で書いたやつのほうが、オーバーライドできる、あとからもアクセサが生成できる、という点ではいい感じ。

感想

ここらへんのメタプログラミングはやっぱりRubyのほうが柔軟性があってかつ、一貫しているので楽かな。
Pythonはやっぱり関数をオブジェクトとして扱いやすい、というのが楽。Rubyのobj.some_methodでメソッドが呼び出せるのは便利だが、その点ではやっぱキツい。ループもPythonほうが好きだな。

ま、やっぱり個人の好み、としかいいようがないなあ。最近ではWindowsの小物はほとんどpythonで書いてるけど(インストーラーがあって関連付けまでしてくれて、楽)、Linuxのほうはそうでもないし。

Related posts:

07.27.08/12am

2 comments

trackback uri
  • ajax-loading
  • ajax-loading
  • ajax-loading
  1. inforno 10.24.06/06pm website
    Python:続・アクセサの生成...

    以前書いてみたアクセサをpropertyを使って改良してみる。

    propertyというのはnew-styleクラスに対してのみ使える、メソッドで

    PLAIN TEX (more...)
  2. Humming Via Kitchen 09.12.07/02am website
    __metaclass__って何?メモ...

    Python の __metaclass__ の理解を深めるメモ。
    Python 和訳ドキュメント の 3.3.3 クラス生成をカスタマイズする を見てみると、

    その (more...)

Leave a Comment

You can use these tags: <code>, <i>, <em>, <strong>, <a>

About

Author:yuin(http://inforno.net/)

文学部文化学科卒という生粋の文系趣味プログラマ。

主にRuby、Javascript、PHP、JAVA,Python,C,Scala,Schemeなどを使っています。今はPythonな感じかもしれない。今後作曲活動なども復活するかもしれない。

Pages