Question: What's the best way in python to create a subclass of an existing class, in such a way that a custom error is raised whenever you attempt to modify the object? The code below shows what I want. class ImmutableModifyError(Exception): pass class ImmutableList(list): def __init__(self, err = "", *argv): self.err = err super().__init__(*argv) def append(self, *argv): raise ImmutableModifyError(self.err) def extend(self, *argv): raise ImmutableModifyError(self.err) def clear(self, *argv): raise ImmutableModifyError(self.err) def insert(self, *argv): raise ImmutableModifyError(self.err) def pop(self, *argv): raise ImmutableModifyError(self.err) def remove(self, *argv): raise ImmutableModifyError(self.err) def sort(self, *argv): raise ImmutableModifyError(self.err) def reverse(self, *argv): raise ImmutableModifyError(self.err)  If I use other immutable types, an AttributeError is thrown instead of the custom error whose message is created along the object. As you can see, this code is too repetitive and would be error-prone if the class changed. I have not taken into account hidden methods and operators. Is there a better way to achieve this?

Answer: There are plenty of more scalable and less error-prone way to achieve this is to dynamically block all mutating methods. Using Setattr to indentify all mutating methods dynamically and override them [Best Method to implement - Pycon 2017] class ImmutableModifyError(Exception): pass class ImmutableList(list): def __init__(self, *args, err="Immutable list cannot be modified"): self.err = err super().__init__(*args) def __raise_error(self, *args, **kwargs): raise ImmutableModifyError(self.err) _mutating_methods = { "append", "extend", "clear", "insert", "pop", "remove", "sort", "reverse", "__setitem__", "__delitem__", "__iadd__", "__imul__" } for met in _mutating_methods: setattr(ImmutableList, method, ImmutableList.__raise_error) ## Don't use map here  Use metaclasses class ImmutableModifyError(Exception): pass class ImmutableMeta(type): def __new__(cls, name, bases, namespace): def raise_error(self, *args, **kwargs): raise ImmutableModifyError(f"Cannot modify {self.__class__.__name__} object") mutating_methods = { attr for base in bases for attr in dir(base) if callable(getattr(base, attr, None)) and attr in {"__setitem__", "__delitem__", "__iadd__", "__imul__", "append", "extend", "clear", "insert", "pop", "remove", "sort", "reverse"} } for method in mutating_methods: namespace[method] = raise_error return super().__new__(cls, name, bases, namespace) class ImmutableList(list, metaclass=ImmutableMeta): pass  One of benefits you get that if you want to use a dict instead of list just one line of code is needed class ImmutableDict(dict, metaclass=ImmutableMeta): pass  Use getitem [most least popular not safe ]  This will make the list completely unusable since even reading values will raise an error. Any iteration (for x in lst) will also fail. This is extreme immutability, where not only modification but even access is prevented.  class ImmutableModifyError(Exception): pass class ImmutableList(list): def __init__(self, *args, err="Immutable list cannot be modified"): self.err = err super().__init__(*args) def __getitem__(self, index): raise ImmutableModifyError(self.err)  One example why it is not good to implement is as it breaks Read-Only Operations and if you override getitem, you won't be able to retrieve values anymore: lst = ImmutableList([1, 2, 3]) print(lst[0]) # Should return 1, but would raise an error instead.