ValueObject implementation in Python


I searched for Value object implementation in Python, and found [keleshev's implementation](https://github.com/keleshev/value) in github. But it doesn't provide the feature of immutability of Value object. So, I implemented property based immutable Value object class by myself.

* explicit property definition
* value based comparison, including hash

Any feedbacks are welcome.

### ValueObject class


    class ValueObject(object):
        """ base class for Value Objects

        please call _set_properties in constructor.
        """

        def __new__(class_, *args, **kwargs):
            self = object.__new__(class_, *args, **kwargs)
            self.__initialized = False
            self.__params = dict()
            return self

        def _set_properties(self, mapping):
            if self.__initialized:
                raise AttributeError('callable only by constructor')

            self.__initialized = True
            self.__params = dict(mapping)
            self.__labels = [k for k, v in mapping]

            def setprop(key):
                setattr(self.__class__, key, property(lambda x: x.__params[key]))

            for k, v in mapping:
                if not hasattr(self.__class__, k):
                    setprop(k)

        def get_values(self):
            return self.__params

        def __repr__(self):
            return unicode(self).encode('utf-8')

        def __unicode__(self):
            return u'%s(%s)' % (
                self.__class__.__name__,
                u', '.join(unicode(self.__params[k]) for k in self.__labels))

        def __hash__(self):
            return hash(repr(self))

        def __eq__(self, other):
            if not isinstance(other, self.__class__):
                return False
            return repr(self) == repr(other)

        def __ne__(self, other):
            if not isinstance(other, self.__class__):
                return True
            return repr(self) != repr(other)


### Test

    # -*- coding: utf-8 -*-
    import pytest

    from . import ValueObject


    class Date(ValueObject):

        def __init__(self, year, month, day):
            self._set_properties([
                ('year', int(year)),
                ('month', int(month)),
                ('day', int(day)),
            ])


    class Foo(ValueObject):
        def __init__(self, text='foo'):
            self._set_properties([
                ('text', text),
            ])


    @pytest.fixture
    def date():
        return Date(2012, 2, 20)


    @pytest.fixture
    def foo_unicode():
        return Foo(u'ふー')


    def test_properties(date):
        assert date.year == 2012
        assert date.month == 2
        assert date.day == 20


    def test_immutable(date):
        with pytest.raises(AttributeError):
            date.year = 2015


    def test_set_properties(date):
        with pytest.raises(AttributeError):
            date._set_properties([
                ('year', 2015),
            ])


    def test_repr(date):
        assert repr(date) == "Date(2012, 2, 20)"


    def test_get_values(date):
        date.get_values == {'year': 2012, 'month': 2, 'day': 20}


    def test_unicode(foo_unicode):
        assert foo_unicode.text == u'ふー'
        assert unicode(foo_unicode) == u"Foo(ふー)"
        assert repr(foo_unicode) == u"Foo(ふー)".encode('utf-8')

### ドメイン駆動開発シリーズ

* [DDDで設計するならCQRSの利用を検討すべき](http://qiita.com/ledmonster/items/22b00c65208dffeff7e4)
* [ValueObject implementation in Python](http://qiita.com/ledmonster/items/2f16d52cdf58d1a73685)
* [Hexagonal Architecture の実践には zope.interface が便利](http://qiita.com/ledmonster/items/9de40b30040326a7dba8)