Seanny123
Seanny123

Reputation: 9346

Immutable/Frozen Dictionary subclass with fixed key, value types

Problem

Similar to previous questions, I would like to create a frozen/immutable dictionary. Specifically, after initialization, the user should get an ValueError when trying to use the __delitem__ and __setitem__ methods.

Unlike the previous questions, I specifically want it to be a sub-class where the initialization type is constrained to a specific key and value type.

Attempted Solution

My own attempts at accomplishing this with collections.UserDict failed:

class WorkflowParams(UserDict):
    def __init__(self, __dict: Mapping[str, str]) -> None:
        super().__init__(__dict=__dict)

    def __setitem__(self, key: str, item: str) -> None:
        raise AttributeError("WorkflowParams is immutable.")

    def __delitem__(self, key: str) -> None:
        raise AttributeError("WorkflowParams is immutable.")

When trying to use it:

workflow_parameters = WorkflowParams(
    {
        "s3-bucket": "my-big-bucket",
        "input-val": "1",
    }
)

It fails with

Traceback (most recent call last):
  File "examples/python_step/python_step.py", line 38, in <module>
    workflow_parameters = WorkflowParams(
  File "/home/sean/git/scargo/scargo/core.py", line 14, in __init__
    super().__init__(__dict=__dict)
  File "/home/sean/miniconda3/envs/scargo/lib/python3.8/collections/__init__.py", line 1001, in __init__
    self.update(kwargs)
  File "/home/sean/miniconda3/envs/scargo/lib/python3.8/_collections_abc.py", line 832, in update
    self[key] = other[key]
  File "/home/sean/git/scargo/scargo/core.py", line 17, in __setitem__
    raise AttributeError("WorkflowParams is immutable.")
AttributeError: WorkflowParams is immutable.

Because of how __init__() resolves methods.

Disqualified Alternatives

Because of my need for a subclass, the commonly suggested solution of using MappingProxyType doesn't meet my requirements.

Additionally, I'm suspicious of answers which recommend subclassing dict, since this seems to cause some unintended behaviour.

Upvotes: 1

Views: 1431

Answers (2)

Copperfield
Copperfield

Reputation: 8510

I would do it with the collections.abc, which to just to quickly build container classes, just implement a couple of thing and done

>>> import collections
>>> class FrozenDict(collections.abc.Mapping):

    def __init__(self,/,*argv,**karg):
        self._data = dict(*argv,**karg)

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

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

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

    def __repr__(self):
        return f"{type(self).__name__}({self._data!r})"

    
>>> t=FrozenDict( {
        "s3-bucket": "my-big-bucket",
        "input-val": "1",
    })
>>> t
FrozenDict({'s3-bucket': 'my-big-bucket', 'input-val': '1'})
>>> t["test"]=23
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    t["test"]=23
TypeError: 'FrozenDict' object does not support item assignment
>>> del t["input-val"]
Traceback (most recent call last):
  File "<pyshell#39>", line 1, in <module>
    del t["input-val"]
TypeError: 'FrozenDict' object does not support item deletion
>>> 

Upvotes: 1

de1
de1

Reputation: 3124

This seem to work just fine for me (tested with Python 3.6 and 3.8):

from collections import UserDict
from typing import Mapping


class WorkflowParams(UserDict):
    def __init__(self, __dict: Mapping[str, str]) -> None:
        super().__init__()
        for key, value in __dict.items():
            super().__setitem__(key, value)

    def __setitem__(self, key: str, item: str) -> None:
        raise AttributeError("WorkflowParams is immutable.")

    def __delitem__(self, key: str) -> None:
        raise AttributeError("WorkflowParams is immutable.")


workflow_parameters = WorkflowParams(
    {
        "s3-bucket": "my-big-bucket",
        "input-val": "1",
    }
)

print(workflow_parameters)
# output: {'s3-bucket': 'my-big-bucket', 'input-val': '1'}

workflow_parameters['test'] = 'dummy'
# expected exception: AttributeError: WorkflowParams is immutable.

Upvotes: 5

Related Questions