4

I'm working on a project and I have a dictionary in Python. Due to this being an "append-only" dictionary, I need a way to disable .pop, .popitem, .clear, etc. Is this possible in python?

I have tried:

mydict = dict()
#attempt 1:
mydict.pop = None
#Get error pop is read-only
#attempt 2:
del mydict.pop
#Get error pop is read-only

I have tried this on all delete methods with the same results.

6
  • 3
    Make a subclass of dict() and override all the methods that can be used to remove items.
    – Barmar
    Jun 24, 2022 at 23:17
  • Good idea, I'll try that. just do class newdict(dict): def pop(): pass I think.
    – Fighter178
    Jun 24, 2022 at 23:19
  • And also __del__, which is used for del mydict[x]
    – Barmar
    Jun 24, 2022 at 23:21
  • and also __delattr_.
    – Fighter178
    Jun 24, 2022 at 23:28
  • 1
    @Sayse: Read-only wouldn't allow any modifications, this question is not asking how to do that.
    – martineau
    Jun 25, 2022 at 0:20

2 Answers 2

4

Using inheritance to remove functionality violates Liskov substitution (a hypothetical dict subclass would behave erroneously when treated as a dict instance). And besides, I can easily just dict.clear(my_subclassed_dict_instance). The thing you're asking for is not, in Python terminology, a dict and shouldn't masquerade as a subclass of one.

What you're looking for is a fresh class entirely. You want a class that contains a dict, not one that is a dict.

import collections.abc.Mapping

class MyAppendOnlyContainer(Mapping):

  def __init__(self, **kwargs):
    self._impl = kwargs

  def __getitem__(self, key):
    return self._impl[key]

  def __setitem__(self, key, value):
    self._impl[key] = value

  def __iter__(self):
    return iter(self._impl)

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

  # Plus whatever other functionality you need ...

Note that collections.abc.Mapping requires __getitem__, __iter__, and __len__ (which your use case faithfully implements) and gives you get, __contains__, and several other useful dict-like helper functions. What you have is not a dict. What you have is a Mapping with benefits.

2

Subclass the dict() class:

class NewDict(dict):
    def clear(self):
        pass
    def pop(self, *args):   #Note: returns a NoneType
        pass
    def popitem(self, *args):
        pass

Now when you create your dict, create it using:

myDict = NewDict()

I quickly dumped this into the command line interpreter and it seems to work.

2
  • 1
    dct=MyDict(); dct[0]=0; dct.pop(0) raises an error (you need the other parameters)
    – 0x263A
    Jun 25, 2022 at 1:24
  • 0x263A: I've made the correction and thank you very much for pointing it out!
    – Ian Moote
    Jun 25, 2022 at 14:33

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.