Easy immutability in python?

Terry Hancock hancock at anansispaceworks.com
Sat Mar 4 21:17:36 CET 2006


On 4 Mar 2006 10:14:56 -0800
jess.austin at gmail.com wrote:
> Since this is a container that needs to be "immutable,
> like a tuple", why not just inherit from tuple?  You'll
> need to override the __new__ method, rather than the
> __init__, since tuples are immutable:
> 
> class a(tuple):
>     def __new__(cls, t):
>         return tuple.__new__(cls, t)

The reason this is undesireable, is because in the typical
use-case, I want to have TWO kinds of objects, one mutable,
and one immutable. Just like tuple / list. I would prefer
not to have to implement them completely separately.  So
far, it's usually simpler to implement the mutable type
first -- the need for immutability semantics usually arises
after you have a mutable version. 

But undoing mutability apparently is like making a sieve
watertight -- it would've been easier to design a ladle and
then put holes in it, than to design a sieve and then stop
up the holes.

In fact, my particular case wants to subclass Set and
ImmutableSet -- their contents, though, are also immutables
in principle.  In fact the elements are similar to "int" or
"str", but I don't want to subclass those, because I don't
want them to have math methods or string methods.

I've considered several different approaches to this. One is
to create a "BaseVocabulary" as an abstract class, from
which the mutable and immutable types are drawn, and then
mix it in with Set and ImmutableSet:

class BaseVocabulary(object):
	def __init__(self, values):
		self._data = values
		# (or something like that)
	# other functionality

class Vocabulary(Set, BaseVocabulary):
	# stuff appropriate for the "open" vocabulary
	pass

class Enumeration(ImmutableSet, BaseVocabulary):
	# stuff appropriate for the "closed" enumeration
	pass

This is bad, both aesthetically and practically.
Aesthetically, because "abstract classes stink of Java" and
pragmatically because the __init__ from BaseVocabulary
will generally not work for the immutable case.

So I either have to rewrite it special, or go back in
time and fix the original, knowing that I'm going to want
an immutable variant.

Again with the aesthetics, it's just ugly that I can't
mutate an object in its __init__ and then make it immutable
after I'm done setting it up. The approach with __new__ and
using the superclass's __setattr__ to set values is nasty
boilerplate-rich cruft which forces me to go back and
completely re-implement the same functionality in a
completely different way, just because of something that
seems like a simple conceptual change (i.e. "don't overwrite
or extend this thing -- make a new one if you need a
change").

I even tried defining __setattr__ at the end of the
__init__, but that doesn't seem to work (or I'm doing it
wrongly).

Right now I'm leaning towards making a class "Immutable"
with all the mutator methods raising NotImplementedError,
and then subclass from this so that it overrides the
necessary methods. It's still going to mess with me, though,
because it will not allow the __init__ to work as planned,
and I'll have to go back and redesign the base class to work
with or without immutability.

Alternatively, I could just decide to change my practice and
implement ALL objects on the assumption that they will be
immutable, and add *mutability* after-the-fact. But that
seems like extraordinarily bad practice, since mutability is
usually the better default.

I'm going to dig through the sets module code to see how it
does this, but what little I've seen gives me a bad feeling
that this simple idea is hard to implement simply in Python
-- and that strikes me as a "wart", yes.

So, I'm wondering if there is a simple way after all.

1) Is there an idiom for this that I just don't know?

2) Have recent changes to the language made it easier?
   (__new__ for example, is apparently new, or at least
   newly documented -- but as I say, I'm not sure it's
   the most practical solution for this problem).

3) Is there a "remarkably clever" way that I can tack
   on, even if it isn't exactly simple?

and only in the unlikely event that answers 1-3 are all
"no", would I ask:

4) And as a last resort, if it really is hard, why?
   Shouldn't a simple idea to express in English be easy to
   express in Python? If it's really impossibly difficult,
   maybe Python should provide a means to implement it.

Cheers,
Terry

-- 
Terry Hancock (hancock at AnansiSpaceworks.com)
Anansi Spaceworks http://www.AnansiSpaceworks.com




More information about the Python-list mailing list