1

I understand the concept of mutable v. immutable objects in Python, no problem. While any immutable object's intrinsic value cannot be modified directly, any instance of an immutable object can be reinstantiated with different values. What I would like to do is build an internal function on a subclass of tuple that can in a controlled fashion, reassign it's own value. This could be basic functionality that I just can't seem to find and would appreciate any assistance.

For example, here is what I'd like to be able to do, but this obviously doesn't work.

class myTuple(tuple):
    def __new__(self):
        initialValue = [1, 2, 3]
        return super(myTuple, self).__new__(self, initialValue)
    def resetMyself(self):
        newValue = [4, 5, 6]
        self = tuple(newValue)

With the following results...

>>> foo = myTuple()
>>> print foo
(1, 2, 3)
>>> foo.resetMyself()
>>> print foo
(4, 5, 6)

From reading a larger number of responses to questions like this on this site, I know some of you may have the tendency to respond with "Why would you want to do this?" but let's save the response space with more direct answers, including possibly "You cannot do that no way, no how," if that's really the case.

Thanks very much all!

EDIT, THANKS FOR THE ANSWER BELOW, HERE IS WHAT I ENDED UP WITH...

class semiImmutableList(list):
    def __setitem__(self, *args):
        raise TypeError("'semiImmutableList' object doesn't support item assignment")
    __setslice__ = __setitem__
    def __delitem__(self, *args):
        raise TypeError("'semiImmutableList' object doesn't support item deletion")
    __delslice__ = __delitem__
    def append(self, *args):
        raise AttributeError("'semiImmutableList' object has no attribute 'append'")
    def extend(self, *args):
        raise AttributeError("'semiImmutableList' object has no attribute 'extend'")
    def insert(self, *args):
        raise AttributeError("'semiImmutableList' object has no attribute 'insert'")
    def remove(self, *args):
        raise AttributeError("'semiImmutableList' object has no attribute 'remove'")
    def pop(self, *args):
        raise AttributeError("'semiImmutableList' object has no attribute 'pop'")
    def __init__(self):
        x = [1, 2, 3]
        super(semiImmutableList, self).__init__(x)
    def resetMyself(self):
        super(semiImmutableList,self).append(5)

Any improvements/adjustments to the above that you can see please post. Seems like the duplication of AttributeError raises could be combined?

sansjoe
  • 262
  • 3
  • 14
  • Not sure what you mean by "any instance of an immutable object can be reinstantiated with different values". New objects with different values can be created and bound to the same name, but an immutable object cannot be changed (hence the term). A "reinstantiated" immutable object is a different object. – martineau Nov 29 '10 at 18:44
  • Yeah, poor phrasing on my part. If x = (1, 2, 3) it can never be changed, but x can become another object, including another tuple with simple reassignment... x = (3, 4, 5). x is x but the first tuple object x referenced is simply gone, not changed. – sansjoe Nov 29 '10 at 19:43
  • def resetMyself(self): super(semiImmutableList,self).append(5) : why using super instead of passing self to a list? I was wondering if your example failure was because you did not returned self, somehow (we had explanation about how to do it, not why it doesn't work) – Ando Jurai Aug 10 '17 at 10:51

2 Answers2

2

If you want a mutable tuple, use a list.

edit:

try this

class FrankenList(object):
    def __init__(self, init=None):
        self.__data = init or []

    def __getitem__(self, key):
        return self.__data[key]

    def __repr__(self):
        return repr(self.__data)

    def __str__(self):
        return str(self.__data)
dan_waterworth
  • 6,261
  • 1
  • 30
  • 41
  • I want the immutability externally. So could I make my list instance publicly immutable while retaining the ability to modify myself internally? – sansjoe Nov 29 '10 at 18:02
  • As long as your didn't provide any methods like ____setitem____, ____delitem____ etc. – Kabie Nov 29 '10 at 18:58
1

Pretty easy, all you have to do is to wrap a list.

class ImmutableList(object):
    def __init__(self, *args):
        self.__values = args; # internally we store the values in a list

    # make imuList[0] = 2 raise an error, just like a tuple would
    def __setitem__(self, index, value):
        raise TypeError('ImmutableList does not support item assignment')

    # del imuList[0] should also raise
    def __delitem__(self, index, value):
        raise TypeError('ImmutableList does not support item deletion')**

    # make our imuList indexable, also catch the normal index error and raise one
    # that tells that this is an immutable list, will make it easier to debug :)
    def __getitem__(self, index):
        try:
            return self.__values[index]

        except IndexError:
            raise IndexError('ImmutableList index out of range')

    # the usual stuff
    def __repr__(self):
        return repr(self.__values)

    def __str__(self):
        return str(self.__values)

# create a new imulist
e = ImmutableList(1, 2, 3, 4)

# works!
print e[0]

# raises an error
e[0] = 5

# raises another error
print e[9]

Now all you have to do is to modify self._values inside the class. One last advise, it's still possible to mess with self._values from the outside, that because Python doesn't support private members.

You can take further measures against the manipulation of __values by subclassing from list directly, but that's more work and one can still fiddle around with the values by using list.__setitem__(imListInstance, 0, 5) and the like.

Community
  • 1
  • 1
Ivo Wetzel
  • 46,459
  • 16
  • 98
  • 112
  • Thanks for this assistance! With this and a couple other sources on the net, I ended up with the code added as an edit to the question which just subclasses list and turns off (hopefully) all of the immutability built in. – sansjoe Nov 29 '10 at 18:55