This post originated from an RSS feed registered with Python Buzz
by Ben Last.
Original Post: You Can't Change Me
Feed Title: The Law Of Unintended Consequences
Feed URL: http://benlast.livejournal.com/data/rss
Feed Description: The Law Of Unintended Consequences
Mutability is interesting. A while ago, someone pointed me to thesediscussions of the essential differences between tuples and lists. Guido's quote: Tuples are for heterogeneous data, list are for homogeneous data. Tuples are *not* read-only lists. On the other hand, one can find plenty of Python references such as this which includes the statement "A tuple is an immutable ordered list of objects". Ditto this from DiveIntoPython; "A tuple is an immutable list". Does "list" in that sentence mean the list type, or the list concept? Perhaps it depends on your point of view, and how much you revere Guido :)
Anyway, a general Van Rossum principle that I tend to like[1] is that where a function returns an object as a result, that object should be immutable. Obviously, like all sweeping generalisations, this is demonstrably wrong in various circumstances, but it's a useful rule of thumb when pondering architectures. Thus I find mutability interesting.
What I'd really like to find is an elegant way to product a list subclass that could be made immutable easily (and doing tuple(myList) is cheating). The most interesting approach would be a class that proxied around an object that starts as a list but then converts that to a tuple when set immutable. Perhaps overriding __getattr__ and catching calls for __methods__ might help... but then there's the risk of someone being clever and saving a reference to a bound method object. More on this, I fear, later.
Meanwhile, here's a snippet of my playing around; a class that uses the inspect module to do a clever thing; it will only allow its attributes to be set by code defined in the same module as the class itself:
#File semimutable.py
#Note that this module imports itself
import inspect, semimutable
class AssignmentError(TypeError):
pass
class SelfMutable(object):
"""An object whose values can only be changed by
its own methods."""
def __setattr__(self, attr, value):
#get code object of caller
c = inspect.currentframe(1).f_code
if not inspect.getmodule(c) == semimutable:
raise AssignmentError, "object is immutable"
object.__setattr__(self, attr, value)
def setTest(self, value):
self.test = value
And here's a little test script that demonstrates it (you'd need to put this in some other module or run it interactively).
import semimutable
d = semimutable.SelfMutable()
d.setTest(2)
print "test is %d" % d.test
d.test = 1
It gives the output:
test is 2
Traceback (most recent call last):
File "C:\Python23\Lib\site-packages\pythonwin\pywin\framework\scriptutils.py", line 310, in RunScript
exec codeObject in __main__.__dict__
File "C:\Documents and Settings\ben\My Documents\ben\python\mutabilitytest.py", line 8, in ?
File "semimutable.py", line 83, in __setattr__
raise AssignmentError, "object is immutable"
AssignmentError: object is immutable
[1] But for which I have no reference I can find at the moment...