少ない記述量で、コンストラクタで指定するフィールドを増減させる(Python)

子クラスが独自に持つフィールドをtupleで指定すると、自動的にコンストラクタで指定できるフィールドに変化するパターンを紹介します。tupleで指定するというのは、このような感じのことです。

  class Child(Parent):
    fields = ('child_field1', 'child_field2')

今回紹介する手法を使った場合、Parentクラスのフィールドがparent_field1だけだったとすると、Childクラスのコンストラクタは__init__(self, parent_field1, child_field1, child_field2)のようになります。fieldの名前に重複は許さないことにすると、実装は次のようになります。

#python2.6
  def test_add_fields_from_tuple(self):
    class MyType(type):
      def __new__(cls, name, bases, d):
	strage = []
	strage.extend(getattr(bases[0], 'fields', ()))# fields comming from base class
	strage.extend(d.get('fields', ()))# fields comming from current class
	assert len(strage) == len(set(strage)), 'layout conflict'
	d['fields'] = tuple(strage)
	return type.__new__(cls, name, bases, d)
    class A(object):
      __metaclass__ = MyType
      fields = ('one',)
      def __init__(self, *fields):
	if len(self.fields) is not len(fields):
	  raise TypeError('%s takes %d arguments' % (self.__class__.__name__,
	                                       len(self.fields))
			 )
        for name, value in itertools.izip(self.fields, fields):
	  setattr(self, name, value)
    self.assertRaises(TypeError, A,)
    a = A(1)
    self.assertEquals(1, a.one)
    class B(A):
      fields = ('two',)
    self.assertRaises(TypeError, B, 1)
    b = B(1,2)
    self.assertEquals(1, b.one)
    self.assertEquals(2, b.two)
    try:
      class C(A):
        fields = ('one', 'two')
    except AssertionError:
      self.assertEqual('layout conflict', str(sys.exc_info()[1]))
      return
    self.fail()