Wrapping big list in Python 2.7 to make it immutable

家住魔仙堡 提交于 2019-12-11 03:19:31

问题


In case I have a really big list (>100k elements) that can be retrieved from some object through function call, is there a way to wrap that list to make it immutable to the caller without copying it to tuple?

In the following example I have only one list field, but the solution should work for any number of list fields.

class NumieHolder(object):

    def __init__(self):
        self._numies = []

    def add(self, new_numie):
        self._numies.append(new_numie)

    @property
    def numies(self):
        # return numies embedded in immutable wrapper/view
        return ??? numies ???

if __name__ == '__main__':
    nh = NumieHolder()

    for numie in xrange(100001): # >100k holds
        nh.add(numie)

    # messing with numies should result in exception
    nh.numies[3] = 4

    # but I still want to use index operator
    print '100th numie:', nh.numies[99]

I would know how to write adapter that behaves that way, but I'm interested if there is already some standard solution (i.e. in standard library or widely known library) I'm not aware of.


回答1:


Unfortunately, there is no such wrapper in the standard library (or other prominent libraries). The main reason is that list is supposed to be a mutable sequence type with index access. The immutable sequence type would be a tuple as you already said yourself. So usually, the standard approach to make a list immutable would be to make it into a tuple by calling tuple(lst).

This is obviously not what you want, as you want to avoid to copy all the elements. So instead, you can create a custom type that wraps the list, and offers all non-modifying methods list also supports:

class ImmutableList:
    def __init__ (self, actualList):
        self.__lst = actualList
    def __len__ (self):
        return self.__lst.__len__()
    def __getitem__ (self, key):
        return self.__lst.__getitem__(key)
    def __iter__ (self):
        return self.__lst.__iter__()
    def __reversed__ (self):
        return self.__lst.__reversed__()
    def __contains__ (self, item):
        return self.__lst.__contains__(item)
    def __repr__ (self):
        return self.__lst.__repr__()
    def __str__ (self):
        return self.__lst.__str__()
>>> original = [1, 2, 3, 4]
>>> immutable = ImmutableList(original)
>>> immutable
[1, 2, 3, 4]
>>> immutable[2]
3
>>> for i in immutable:
        print(i, end='; ')

1; 2; 3; 4; 
>>> list(reversed(immutable))
[4, 3, 2, 1]
>>> immutable[1] = 4
Traceback (most recent call last):
  File "<pyshell#39>", line 1, in <module>
    immutable[1] = 4
TypeError: 'ImmutableList' object does not support item assignment

The alternative would be to subtype list and override __setitem__ and __delitem__ to raise an exception instead, but I would suggest against that, as a subtype of list would be expected to have the same interface as list itself. ImmutableList above on the other hand is just some indexable sequence type which happens to wrap a real list itself. Apart from that, having it as a subtype of list would actually require you to copy the contents once, so wrapping is definitely better if you don’t want to recreate all those items (which seems to be your point—otherwise you could just use tuple).




回答2:


See Emulating Container Types for the "special" methods you'd want to implement or override. Namely, you'd want to implement __setitem__ and __delitem__ methods to raise an exception, so the list cannot be modified.



来源:https://stackoverflow.com/questions/21000994/wrapping-big-list-in-python-2-7-to-make-it-immutable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!