MappingProxyType 和 PEP 416 freezedict 之间的区别

问题描述 投票:0回答:4

虽然

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不能被子类化,因此需要组合)和方法转发)。

python python-3.x immutability
4个回答
31
投票

TL;博士

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 比较性能中受益。


12
投票

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

3
投票

TL;DR 我建议你使用frozendict。 Github:https://github.com/Marco-Sulla/python-frozendict

MappingProxyType
曾经非常慢。现在已经没有了,但是相比
frozendict
还是有一些不足:

  1. 它没有哈希值,所以你不能在
    set
    dict
    等中使用它。我记得在某个时候它引入了一个“假”哈希值,但似乎它被删除了(我检查过它在 Python 3.13 中)。
  2. 如果
    frozendict
    具有哈希值,则任何返回浅拷贝的操作都只会返回
    frozendict
    本身。相反,
    MappingProxyType
    执行内部
    dict
    的普通复制,所以速度慢很多
  3. frozendict
    为函数式编程风格提供了一些额外的加速和功能,如
    set
    delete
    方法以及
    deepfreeze
    模块函数,用于将对象转换为不可变对象,也转换任何嵌套的可变对象。

PS:我是包裹的新主人


1
投票

我注意到的一件事是

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)

注意:这两个不可变映射都不支持“删除并返回新的不可变副本”操作。

© www.soinside.com 2019 - 2024. All rights reserved.