Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 416 – 添加一个内置的 frozendict 类型

作者:
Victor Stinner <vstinner at python.org>
状态:
已拒绝
类型:
标准跟踪
创建日期:
2012年2月29日
Python 版本:
3.3

目录

拒绝通知

我拒绝此 PEP。原因有(不详尽)

  • 根据 Raymond Hettinger 的说法,frozendict 的使用率很低。那些使用它的人倾向于将其仅用作提示,例如声明全局或类级别的“常量”:它们并非真正不可变,因为任何人仍然可以为该名称赋值。
  • 存在避免可变默认值的现有习惯用法。
  • 在 PyPy 中使用 frozendict 优化代码的潜力是不确定的;许多其他事情需要先改变。编译时查找一般也是如此。
  • 多个线程可以通过约定不同意修改共享的字典,没有太大的强制需求。多个进程无法共享字典。
  • 许多人反对用 Python 编写安全沙箱,即使范围有限,因为永远无法证明沙箱实际上是安全的。因此,我们近期不会将其添加到标准库中,因此这种用例超出了 PEP 的范围。

另一方面,将现有的只读字典代理暴露为内置类型对我来说听起来不错。(它需要被修改为允许调用构造函数。)GvR。

更新(2012-04-15):Python 3.3 的 types 模块中添加了一个新的 MappingProxyType 类型。

摘要

添加一个新的内置 frozendict 类型。

基本原理

frozendict 是一个只读映射:不能添加或删除键,并且键始终映射到相同的值。但是,frozendict 的值可能不可哈希。frozendict 仅当所有值都可哈希时才可哈希。

用例

  • 不可变全局变量,如默认配置。
  • 函数参数的默认值。避免可变默认参数的问题。
  • 实现缓存:frozendict 可用于存储函数关键字。frozendict 可用作映射的键或集合的成员。
  • frozendict 避免了在 frozendict 被多个线程或进程共享时需要锁,特别是可哈希的 frozendict。它还有助于禁止协程(生成器 + greenlet)修改全局状态。
  • 由于映射是只读的,因此可以在编译时而不是运行时进行 frozendict 查找。frozendict 可用于代替预处理器来在编译时删除条件代码,例如特定于调试构建的代码。
  • frozendict 有助于为安全模块实现只读对象代理。例如,可以使用 frozendict 类型作为 `__builtins__` 映射或 `type.__dict__`。这是可能的,因为 frozendict 与 PyDict C API 兼容。
  • frozendict 在某些情况下可以避免使用只读代理。frozendict 比代理快,因为在 frozendict 中获取项是一个快速查找,而代理需要函数调用。

限制

  • frozendict 必须实现 Mapping 抽象基类
  • frozendict 的键和值可以是无序的
  • 仅当所有键和值都可哈希时,frozendict 才可哈希
  • frozendict 的哈希不依赖于项的创建顺序

实施

  • 添加一个基于 PyDictObject 的 PyFrozenDictObject 结构,并带有一个额外的“Py_hash_t hash;”字段
  • frozendict.__hash__() 使用 hash(frozenset(self.items())) 实现,并将结果缓存到其私有的 hash 属性中
  • 将 frozendict 注册为 collections.abc.Mapping
  • frozendict 可与 PyDict_GetItem() 一起使用,但 PyDict_SetItem() 和 PyDict_DelItem() 会引发 TypeError

方案:可哈希的字典

为了确保 frozendict 可哈希,可以在创建 frozendict 之前检查值

import itertools

def hashabledict(*args, **kw):
    # ensure that all values are hashable
    for key, value in itertools.chain(args, kw.items()):
        if isinstance(value, (int, str, bytes, float, frozenset, complex)):
            # avoid the compute the hash (which may be slow) for builtin
            # types known to be hashable for any value
            continue
        hash(value)
        # don't check the key: frozendict already checks the key
    return frozendict.__new__(cls, *args, **kw)

反对意见

namedtuple 可能符合 frozendict 的要求。

namedtuple 不是映射,它不实现 Mapping 抽象基类。

frozendict 可以使用描述符用 Python 实现”并且“frozendict 只需要在实践中是常量。”

如果 frozendict 用于加固 Python(出于安全目的),则必须用 C 实现。用 C 实现的类型也更快。

PEP 351 被拒绝了。

PEP 351 尝试冻结一个对象,因此可能会将可变对象转换为不可变对象(使用不同的类型)。frozendict 不会转换任何东西:如果值不可哈希,hash(frozendict) 会引发 TypeError。冻结对象不是此 PEP 的目的。

替代方案:dictproxy

Python 有一个内置的 dictproxy 类型,由 type.__dict__ getter 描述符使用。此类型不是公开的。dictproxy 是字典的只读视图,但它不是只读映射。如果修改了字典,dictproxy 也会被修改。

可以使用 ctypes 和 Python C API 来使用 dictproxy,例如,请参阅 Ikkei Shimomura 的 通过 ctypes.pythonapi 和 type() 创建 dictproxy 对象(Python 菜谱 576540)。该菜谱包含一个测试,用于检查 dictproxy 是否“可变”(修改链接到 dictproxy 的字典)。

然而,dictproxy 在某些情况下可能有用,当其可变属性不是问题时,可以避免复制字典。

现有实现

白名单方法。

  • Aristotelis Mikropoulos 的 实现不可变字典(Python 菜谱 498072)。与 frozendict 类似,只是它不是真正只读的:可以访问其私有的内部字典。它不实现 `__hash__` 并且存在实现问题:可以再次调用 `__init__()` 来修改映射。
  • PyWebmail 包含一个 ImmutableDict 类型:webmail.utils.ImmutableDict。仅当键和值都可哈希时,它才可哈希。它不是真正只读的:其内部字典是公共属性。
  • remember 项目:remember.dicts.FrozenDict。它用于实现缓存:FrozenDict 用于存储函数回调。FrozenDict 可能可哈希。它有一个额外的 `supply_dict()` 类方法,用于从字典创建 FrozenDict 而无需复制字典:将字典存储为内部字典。实现问题:可以调用 `__init__()` 来修改映射,并且哈希可能因项的创建顺序而异。映射不是真正只读的:内部字典可以在 Python 中访问。

黑名单方法:继承自 dict 并重写写入方法以引发异常。它不是真正只读的:仍然可以对这种“冻结字典”调用 dict 方法来修改它。

可哈希字典:继承自 dict 并仅添加 `__hash__` 方法。


来源:https://github.com/python/peps/blob/main/peps/pep-0416.rst

最后修改:2025-02-01 08:59:27 GMT