Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

ValueObject implementation in Python

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

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
3
Help us understand the problem. What are the problem?