Reputation: 52323
While frozendict
was rejected, a related class types.MappingProxyType
was added to public API in python 3.3.
I understand MappingProxyType
is just a wrapper around the underlying dict
, but despite that isn't it functionally equivalent to frozendict
?
In other words, what's the substantive difference between the original PEP 416 frozendict
and this:
from types import MappingProxyType
def frozendict(*args, **kwargs):
return MappingProxyType(dict(*args, **kwargs))
Of course MappingProxyType
is not hashable as is, but just as the PEP suggested for frozendict
, it can be made hashable after ensuring that all its values are hashable (MappingProxyType cannot be subclassed, so it would be require composition and forwarding of methods).
Upvotes: 47
Views: 19893
Reputation: 15930
TL;DR I suggest you to use frozendict. Github: https://github.com/Marco-Sulla/python-frozendict
MappingProxyType
once was terribly slow. Now it's no more, but has still some lacks compared to frozendict
:
set
, dict
etc. I remember that at a certain point it was introduced a "fake" hash, but it seems it was removed (I checked it in Python 3.13).frozendict
has an hash, any operation that returns a shallow copy simply returns the frozendict
itself. On the contrary, MappingProxyType
performs a normal copy of the internal dict
, so it's much more slowfrozendict
has some additional speedups and features for a functional programming style, as a set
and delete
methods and a deepfreeze
module function, for converting an object in its immutable counterpart, converting also any nested mutable object.PS: I'm the new owner of the package
Upvotes: 3
Reputation: 1314
MappingProxyType add immutability only on a first level:
>>> from types import MappingProxyType
>>> d = {'a': {'b': 1}}
>>> md = MappingProxyType(d)
>>> md
mappingproxy({'a': {'b': 1}})
>>> md['a']['b']
1
>>> md['a']['b'] = 3
>>> md['a']['b']
3
Upvotes: 12
Reputation: 492
One thing I've noticed is that frozendict.copy
supports add/replace (limited to string keys), whereas MappingProxyType.copy
does not. For instance:
d = {'a': 1, 'b': 2}
from frozendict import frozendict
fd = frozendict(d)
fd2 = fd.copy(b=3, c=5)
from types import MappingProxyType
mp = MappingProxyType(d)
# mp2 = mp.copy(b=3, c=5) => TypeError: copy() takes no keyword arguments
# to do that w/ MappingProxyType we need more biolerplate
temp = dict(mp)
temp.update(b=3, c=5)
mp2 = MappingProxyType(temp)
Note: none of these two immutable maps supports "remove and return new immutable copy" operation.
Upvotes: 1
Reputation: 3754
MappingProxyType
is a read only proxy for mapping (e.g. dict) objects.
frozendict
is an immutable dict
The proxy pattern is (quoting wikipedia):
A proxy, in its most general form, is a class functioning as an interface to something else.
The MappingProxyType
is just a simple proxy (i.e. interface) to access the real object (the real map, which on our example is dict).
the suggested frozendict
object is just as set is to frozenset. a read only (immutable) object which can only be changed upon creation.
So why do we need MappingProxyType
? example use case is where you want to pass a dictionary to another function but without it able to change your dictionary, it act as a read only proxy, (quoting python docs):
Read-only proxy of a mapping. It provides a dynamic view on the mapping’s entries, which means that when the mapping changes, the view reflects these changes.
lets see some example usage of the MappingProxyType
In [1]: from types import MappingProxyType
In [2]: d = {'a': 1, 'b': 2}
In [3]: m = MappingProxyType(d)
In [4]: m['a']
Out[4]: 1
In [5]: m['a'] = 5
TypeError: 'mappingproxy' object does not support item assignment
In [6]: d['a'] = 42
In [7]: m['a']
Out[7]: 42
In [8]: for i in m.items():
...: print(i)
('a', 42)
('b', 2)
because the PEP did not make it into python, we cannot know for sure what the implementation that would be. by looking at the PEP we see that:
frozendict({'a': {'b': 1}})
would raise an exception as {'b': 1}
is not hashable value, but on your implementation it will create the object. of-course, you can add a validation for the value as noted on the PEP.
I assume part of the PEP was memory optimization and implementation of this kind of frozendict could have benefit from the performance of dict comparison using the __hash__
implementation.
Upvotes: 35