虽然
frozendict
被拒绝了,但相关类 types.MappingProxyType
在 python 3.3 中被添加到公共 API 中。
我知道
MappingProxyType
只是底层 dict
的包装,但尽管如此,它在功能上不等于 frozendict
吗?
也就是说,原来的PEP 416
frozendict
和这个有什么本质区别:
from types import MappingProxyType
def frozendict(*args, **kwargs):
return MappingProxyType(dict(*args, **kwargs))
当然,
MappingProxyType
本身是不可散列的,但正如frozendict
建议的PEP一样,在确保其所有值都是可散列的之后,它可以变得可散列(MappingProxyType不能被子类化,因此需要组合)和方法转发)。
MappingProxyType
是映射(例如字典)对象的只读代理。
frozendict
是一个不可变的字典
代理模式是(引用wikipedia):
代理,以其最一般的形式,是一个充当代理的类 与其他东西的接口。
MappingProxyType
只是一个简单的代理(即接口)来访问真实对象(真实地图,在我们的示例中是字典)。
建议的
frozendict
对象就像 set 到 freezeset 一样。只读(不可变)对象,只能在创建时更改。
那么我们为什么需要
MappingProxyType
?示例用例是您想要将字典传递给另一个函数,但它无法更改您的字典,它充当只读代理,(引用python文档):
映射的只读代理。它提供了动态视图 映射的条目,这意味着当映射发生变化时,视图 反映了这些变化。
让我们看看
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)
因为 PEP 没有进入 python,我们无法确定它的实现是什么。 通过查看 PEP,我们可以看到:
frozendict({'a': {'b': 1}})
会引发异常,因为
{'b': 1}
不是可散列值,但在您的实现中它将创建该对象。当然,您可以为 PEP 上注明的值添加验证。
我假设 PEP 的一部分是内存优化,并且这种 freezedict 的实现可以从使用
__hash__
实现的 dict 比较性能中受益。
MappingProxyType 仅在第一级添加不变性:
>>> 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
TL;DR 我建议你使用frozendict。 Github:https://github.com/Marco-Sulla/python-frozendict
MappingProxyType
曾经非常慢。现在已经没有了,但是相比frozendict
还是有一些不足:
set
,dict
等中使用它。我记得在某个时候它引入了一个“假”哈希值,但似乎它被删除了(我检查过它在 Python 3.13 中)。frozendict
具有哈希值,则任何返回浅拷贝的操作都只会返回 frozendict
本身。相反,MappingProxyType
执行内部dict
的普通复制,所以速度慢很多frozendict
为函数式编程风格提供了一些额外的加速和功能,如 set
和 delete
方法以及 deepfreeze
模块函数,用于将对象转换为不可变对象,也转换任何嵌套的可变对象。 PS:我是包裹的新主人
我注意到的一件事是
frozendict.copy
支持添加/替换(仅限于字符串键),而 MappingProxyType.copy
不支持。例如:
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)
注意:这两个不可变映射都不支持“删除并返回新的不可变副本”操作。