Did I overuse / underuse threading locks in my custom list class? I am writing a Python module in which two threads access one list. One thread adds 500 items to the list per second, and the other thread reads the list at an irregular interval. I want to make a thread-safe "list" class to avoid having to use locks every time I read or write to the list (suggested by this answer to a previous question on SO).

Here is my first go at a thread-safe list class (with help from these previous SO answers: 1 and 2). Are there any methods that should be locked that are not currently locked, or any methods that do not require a lock that are currently locked?

import collections
import threading

class ThreadSafeList(collections.MutableSequence):
    """Thread-safe list class."""
    def __init__(self, iterable=None):
        if iterable is None:
            self._list = list()
        else:
            self._list = list(iterable)
        self.rlock = threading.RLock()

    def __len__(self): return len(self._list)

    def __str__(self): return self.__repr__()

    def __repr__(self): return "{}".format(self._list)

    def __getitem__(self, i): return self._list[i]

    def __setitem__(self, index, value):
        with self.rlock:
            self._list[index] = value

    def __delitem__(self, i): 
        with self.rlock:
            del self._list[i]

    def __iter__(self):
        with self.rlock:
            for elem in self._list:
                yield elem

    def insert(self, index, value): 
        with self.rlock:
            self._list.insert(index, value)
While it looked like at first glance that you were missing quite a few from the normal list methods:

&gt;&gt;&gt; dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']


Inheriting from collections.MutableSequence takes care of most of them (by using internally the re-implemented methods instead of the C methods).

&gt;&gt;&gt; l = ThreadSafeList([3, 2, 1, 1])


A few are still missing, though:

&gt;&gt;&gt; l.sort()
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
AttributeError: 'ThreadSafeList' object has no attribute 'sort'

&gt;&gt;&gt; l = l + [1]
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
TypeError: unsupported operand type(s) for +: 'ThreadSafeList' and 'list'

&gt;&gt;&gt; l = l + l
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
TypeError: unsupported operand type(s) for +: 'ThreadSafeList' and 'ThreadSafeList'

&gt;&gt;&gt; l = l * 3
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
TypeError: unsupported operand type(s) for *: 'ThreadSafeList' and 'int'


Although, there seem to be work-arounds for all of them, if changing mutating and non-mutating is fine:

&gt;&gt;&gt; l = ThreadSafeList(sorted(l))
&gt;&gt;&gt; l.extend([1])
&gt;&gt;&gt; l.extend(l)
&gt;&gt;&gt; l = ThreadSafeList(list(l) * 3)


Note that there is no way to add these lists without mutating them.

Otherwise, I see only one style-issue:

The output of repr should ideally include the class, so that eval(repr(obj)) returns a copy of the instance. You can define it like this:

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

def __repr__(self):
    return "{self.__class__.__name__}({self._list})".format(self=self)


The latter can be slightly simplified in Python 3.6, using f-strings:

def __repr__(self):
    return f"{self.__class__.__name__}({self._list})"