LoginSignup
8
3

More than 5 years have passed since last update.

ValueObject implementation in Python

Last updated at Posted at 2015-01-15

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.

  • 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')

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

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3