5

I'm trying to make a list which is used throughout the application immutable. I thought wrapping this list in a tuple would do the trick, but it seems that tuple(list) doesn't actually wrap, but copies the list elements.

>>> a = [1, 2, 3, 4]
>>> b = tuple(a)
>>> b
(1, 2, 3, 4)
>>> a[0] = 2
>>> b # was hoping b[0] to be 2
(1, 2, 3, 4)

Is there an easy way of creating a list-backed "view" on this list that is immutable (wrt. operations on this view), but reflects any change that happened to the backing list?

I realise that this question has been asked before, but none of the responses address this view-backing list relationship (in fact some of the comments even suggest that tuples work the way I was hoping they do, but the above snippet suggests otherwise).

4
  • 1
    So, basically, you want it to be selectively mutable? That's kind of hard to enforce. What problem are you actually trying to solve here? Commented Mar 12, 2014 at 2:12
  • 2
    Why do you want the list to be immutable? It might be preferable to just state in the documentation that the list should not be changed. If the user changes it, that's his or her fault. Commented Mar 12, 2014 at 2:29
  • I really am curious about the use case here...sounds interesting. Commented Mar 12, 2014 at 2:52
  • @PaulBecotte: The main reason was some refactoring for which I wanted to restrict access to a list member variable that I previously directly used. Internally, I still use a list and mutate it, but every time I change it, I don't necessarily want to be reminded to also update its external interface method (I use @property instead of the original member variable now). Commented Mar 12, 2014 at 12:34

1 Answer 1

12

If you don't want to copy the data, and want to pass an unchangeable "list" around, one way to do so is to create a proxy object, copy of a list, which disables all changing methods, and refer the reading methods to the original list - something along:

from collections import UserList

class ReadOnlyList(UserList):
    def __init__(self, original):
        self.data = original
    def insert(self, index=None, value=None):
        raise TypeError()
    __setitem__ = insert
    __delitem__ = insert
    append = insert
    extend = insert
    pop = insert
    reverse = insert
    sort = insert

By subclassing "UserList" one ensures all code dealing with the list data will go through the publicly exposed Python methods, and better yet, all the remaining methods are already implemented and proxy to the internal data attribute.

bellow, the original answer from 2014, focusing on Python 2


    class ReadOnlyList(list):
        def __init__(self, other):
            self._list = other
        
        def __getitem__(self, index):
            return self._list[index]
        
        def __iter__(self):
            return iter(self._list)
        
        def __slice__(self, *args, **kw):
            return self._list.__slice__(*args, **kw)
        
        def __repr__(self):
            return repr(self._list)

        def __len__(self):
            return len(self._list)
        
        def NotImplemented(self, *args, **kw):
            raise ValueError("Read Only list proxy")
        
        append = pop = __setitem__ = __setslice__ = __delitem__ = NotImplemented

And, of course, implement whatever other methods you judge necessary, either raising the error (or ignoring the writting instruction) - or acessing the corresponding object in the internal list.

Sign up to request clarification or add additional context in comments.

5 Comments

The O.P. wants to keep changing the original list in other contexts, by what I understand of the question. Therefore it is unhashable by definition. If one wants a hash of the snapshot of the momment it wraps the inner list, the solution is to create a hash value that temporarily creates a tuple and picks that hash, and return that value on a call to __hash__.
@eyquem: I just did post it. And clicked on edit following the publishing of the post, to type in the example code.
Thanks @jsbueno. Too bad that no 'one line'-Python idiom or a special list came out of as a response, but this certainly does the trick. Thanks again.
@jsbueno: One method you need to add to ReadOnlyList is __len__, otherwise the length is always 0.
@orange: yes, for sure. I've noticed it when playing around with this code, afterwars, but did not update the answer here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.