PythonでRubyのStruct風の構造体を実現する
Rubyでは,Structクラスを用いることによって,特定のフィールドを持つクラスを簡単に作成することができます。
Foo = Struct.new("Foo", "foo", "bar") f = Foo.new(1, 2) p f.foo # 1 p f.bar # 2
一方、Pythonの場合、namedtupleというライブラリで同様の機能が提供されています。
namedtupleはPython2.6以降で利用可能です。
from collections import namedtuple Foo = namedtuple("Foo", "foo bar") f = Foo(1, 2) print f.foo # 1 print f.bar # 2
ただし、Pythonではtupleがイミュータブル(書き換え不可能)なオブジェクトであるのと同様に、このnamedtupleも属性の値を変更することができません。
f.foo = 0 # AttributeError: can't set attribute
Pythonではクラス定義の中で__slots__という名前の変数に値を入れることによって、特定の名前の属性のみを持つクラスを作成することができます。
class Foo(object): __slots__ = ("foo", "bar") f = Foo() try: f.foo = 1 f.bar = 2 f.hoge = 3 except AttributeError, e: print e # 'Foo' object has no attribute 'hoge'
これを利用することで、RubyのStructのように特定の名前の属性のみを持ち、書き換え可能なオブジェクトのクラスを実現することができます。
from itertools import chain, repeat, izip def Struct(name, *fields): def __init__(self, *args): if len(args) > len(fields): raise TypeError("__init__() takes at most %d arguments (%d given)" % (len(fields)+1, len(args)+1)) args = chain(args, repeat(None)) for field, value in izip(fields, args): setattr(self, field, value) attrs = dict() attrs["__slots__"] = fields attrs["__init__"] = __init__ return type(name, (object,), attrs) if __name__ == "__main__": Foo = Struct("Foo", "foo", "bar") f = Foo(1, 2) print "f.foo =", f.foo print "f.bar =", f.bar f.foo = 0 # OK try: f.baz = 0 # NG except AttributeError, e: print e
実行結果は以下のようになります。
f.foo = 1 f.bar = 2 'Foo' object has no attribute 'baz'