Python中的不可变列表

5 投票
1 回答
4586 浏览
提问于 2025-04-17 21:52

我想在整个应用中使用一个不可变的列表。我以为把这个列表放在一个元组里就可以实现这个目的,但实际上,tuple(list)并不是把列表包裹起来,而是复制了列表里的元素。

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

有没有简单的方法可以创建一个基于这个列表的“视图”,这个视图是不可变的(也就是说对这个视图的操作不会改变它),但又能反映出原列表的任何变化呢?

我知道这个问题之前有人问过,但没有一个回答真正解决这个视图和原列表之间的关系(实际上,有些评论甚至建议元组的工作方式和我希望的一样,但上面的代码片段却表明并非如此)。

1 个回答

12

如果你不想复制数据,而是想传递一个不可更改的“列表”,一种方法是创建一个代理对象,这个代理对象是列表的一个副本,它禁用所有可以改变内容的方法,同时把读取的方法指向原始列表。大概就是这样:

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

通过继承“UserList”,可以确保所有处理列表数据的代码都会通过公开的Python方法来执行,更棒的是,其他的方法已经实现,并且会指向内部的data属性。

下面是2014年原始的回答,主要针对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.

撰写回答