I searched for Value object implementation in Python, and found keleshev's implementation 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.
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'hu ')
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'huh ' Assert Unicode (foo_unicode) == u "Foo" assert repr (foo_unicode) == u "Foo" .encode ('utf-8')
Recommended Posts